or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-font-operations.mddrawing-pens.mdfont-building.mdfont-processing.mdindex.mdutilities-tools.mdvariable-fonts.md

font-building.mddocs/

0

# Font Building

1

2

Build fonts from scratch using a builder pattern, supporting both TrueType and CFF/PostScript font creation with comprehensive table setup methods. FontBuilder provides a structured approach to creating fonts programmatically with proper table dependencies and validation.

3

4

## Capabilities

5

6

### FontBuilder Class

7

8

The primary class for constructing fonts from scratch using a builder pattern with sequential setup methods.

9

10

```python { .api }

11

class FontBuilder:

12

def __init__(self, unitsPerEm=None, font=None, isTTF=True):

13

"""

14

Initialize font builder.

15

16

Parameters:

17

- unitsPerEm: int, units per em (default: 1000)

18

- font: TTFont, existing font to modify (default: new font)

19

- isTTF: bool, True for TrueType, False for CFF/PostScript

20

"""

21

22

def setupGlyphOrder(self, glyphOrder):

23

"""

24

Set glyph names list (must be called first).

25

26

Parameters:

27

- glyphOrder: List[str], ordered list of glyph names

28

"""

29

30

def setupCharacterMap(self, cmap):

31

"""

32

Set character-to-glyph mapping.

33

34

Parameters:

35

- cmap: Dict[int, str], Unicode codepoint to glyph name mapping

36

"""

37

38

def setupGlyf(self, glyphs):

39

"""

40

Set TrueType glyph data (for TrueType fonts).

41

42

Parameters:

43

- glyphs: Dict[str, Glyph], glyph name to glyph object mapping

44

"""

45

46

def setupCFF(self, psName, charStrings, charStringsType=2):

47

"""

48

Set CFF/PostScript data (for OpenType fonts).

49

50

Parameters:

51

- psName: str, PostScript font name

52

- charStrings: Dict[str, CharString], glyph name to CharString mapping

53

- charStringsType: int, CharString format version (1 or 2)

54

"""

55

56

def setupHorizontalMetrics(self, metrics):

57

"""

58

Set glyph advance widths and left side bearings.

59

60

Parameters:

61

- metrics: Dict[str, Tuple[int, int]], glyph name to (advance, lsb) mapping

62

"""

63

64

def setupHorizontalHeader(self, ascent=None, descent=None):

65

"""

66

Set horizontal metrics header.

67

68

Parameters:

69

- ascent: int, font ascent (default: calculated from metrics)

70

- descent: int, font descent (default: calculated from metrics)

71

"""

72

73

def setupNameTable(self, nameStrings, mac=True):

74

"""

75

Set font naming information.

76

77

Parameters:

78

- nameStrings: Dict[str, str], name ID to string mapping

79

- mac: bool, include Mac platform names

80

"""

81

82

def setupOS2(self, sTypoAscender=None, sTypoDescender=None, usWinAscent=None, usWinDescent=None):

83

"""

84

Set OS/2 table with font metrics and properties.

85

86

Parameters:

87

- sTypoAscender: int, typographic ascender

88

- sTypoDescender: int, typographic descender

89

- usWinAscent: int, Windows ascent

90

- usWinDescent: int, Windows descent

91

"""

92

93

def setupPost(self, keepGlyphNames=True, extraNames=[]):

94

"""

95

Set PostScript table.

96

97

Parameters:

98

- keepGlyphNames: bool, include glyph names in font

99

- extraNames: List[str], additional glyph names beyond standard set

100

"""

101

```

102

103

#### Basic Font Building Example

104

105

```python

106

from fontTools.fontBuilder import FontBuilder

107

from fontTools.pens.ttGlyphPen import TTGlyphPen

108

109

# Initialize builder for TrueType font

110

fb = FontBuilder(unitsPerEm=1000, isTTF=True)

111

112

# 1. Set glyph order (must be first)

113

glyph_order = [".notdef", "A", "B", "space"]

114

fb.setupGlyphOrder(glyph_order)

115

116

# 2. Set character mapping

117

cmap = {

118

ord('A'): 'A',

119

ord('B'): 'B',

120

ord(' '): 'space'

121

}

122

fb.setupCharacterMap(cmap)

123

124

# 3. Create glyphs using pen

125

glyphs = {}

126

127

# Create .notdef glyph

128

pen = TTGlyphPen(None)

129

pen.moveTo((100, 0))

130

pen.lineTo((100, 750))

131

pen.lineTo((400, 750))

132

pen.lineTo((400, 0))

133

pen.closePath()

134

glyphs[".notdef"] = pen.glyph()

135

136

# Create letter A

137

pen = TTGlyphPen(None)

138

pen.moveTo((200, 0))

139

pen.lineTo((100, 700))

140

pen.lineTo((150, 700))

141

pen.lineTo((300, 700))

142

pen.lineTo((350, 700))

143

pen.lineTo((250, 0))

144

pen.closePath()

145

# Add crossbar

146

pen.moveTo((175, 350))

147

pen.lineTo((275, 350))

148

pen.lineTo((275, 400))

149

pen.lineTo((175, 400))

150

pen.closePath()

151

glyphs["A"] = pen.glyph()

152

153

# Simple rectangle for B

154

pen = TTGlyphPen(None)

155

pen.moveTo((100, 0))

156

pen.lineTo((100, 700))

157

pen.lineTo((300, 700))

158

pen.lineTo((300, 0))

159

pen.closePath()

160

glyphs["B"] = pen.glyph()

161

162

# Empty glyph for space

163

pen = TTGlyphPen(None)

164

glyphs["space"] = pen.glyph()

165

166

fb.setupGlyf(glyphs)

167

168

# 4. Set metrics (advance width, left side bearing)

169

metrics = {

170

".notdef": (500, 100),

171

"A": (450, 100),

172

"B": (400, 100),

173

"space": (250, 0)

174

}

175

fb.setupHorizontalMetrics(metrics)

176

177

# 5. Set font header

178

fb.setupHorizontalHeader(ascent=800, descent=-200)

179

180

# 6. Set font names

181

names = {

182

"familyName": "TestFont",

183

"styleName": "Regular",

184

"uniqueFontIdentifier": "TestFont-Regular",

185

"fullName": "TestFont Regular",

186

"psName": "TestFont-Regular",

187

"version": "1.0"

188

}

189

fb.setupNameTable(names)

190

191

# 7. Set OS/2 table

192

fb.setupOS2(sTypoAscender=800, sTypoDescender=-200, usWinAscent=800, usWinDescent=200)

193

194

# 8. Set PostScript table

195

fb.setupPost()

196

197

# Get the built font

198

font = fb.font

199

font.save("test_font.ttf")

200

```

201

202

### Variable Font Setup

203

204

Additional methods for creating variable fonts with multiple axes and instances.

205

206

```python { .api }

207

def setupFvar(self, axes, instances):

208

"""

209

Set up font variations (variable font axes and instances).

210

211

Parameters:

212

- axes: List[Dict], axis definitions with name, tag, min, default, max values

213

- instances: List[Dict], named instances with coordinates and names

214

"""

215

216

def setupAvar(self, axes):

217

"""

218

Set up axis variation mapping (non-linear axis mapping).

219

220

Parameters:

221

- axes: Dict[str, List[Tuple[float, float]]], axis tag to mapping pairs

222

"""

223

224

def setupGvar(self, variations):

225

"""

226

Set up glyph variations (delta coordinates for each axis).

227

228

Parameters:

229

- variations: Dict[str, Dict], glyph name to variation deltas mapping

230

"""

231

```

232

233

#### Variable Font Example

234

235

```python

236

# Build variable font with weight axis

237

fb = FontBuilder(unitsPerEm=1000, isTTF=True)

238

239

# ... basic setup steps as above ...

240

241

# Define weight axis

242

axes = [{

243

'name': 'Weight',

244

'tag': 'wght',

245

'minValue': 100,

246

'defaultValue': 400,

247

'maxValue': 900

248

}]

249

250

# Define instances

251

instances = [{

252

'name': 'Light',

253

'coordinates': {'wght': 200}

254

}, {

255

'name': 'Regular',

256

'coordinates': {'wght': 400}

257

}, {

258

'name': 'Bold',

259

'coordinates': {'wght': 700}

260

}]

261

262

fb.setupFvar(axes, instances)

263

264

# Set up glyph variations (simplified example)

265

variations = {

266

'A': {

267

'wght': [

268

# Delta coordinates for different weight values

269

{'coordinates': [10, 0, 15, 0, 20, 0], 'support': [0, 1, 1]}

270

]

271

}

272

}

273

fb.setupGvar(variations)

274

```

275

276

### Color Font Setup

277

278

Methods for creating color fonts using COLR/CPAL tables.

279

280

```python { .api }

281

def setupCOLR(self, colorLayers):

282

"""

283

Set up color layer definitions (COLR table).

284

285

Parameters:

286

- colorLayers: Dict[str, List[Tuple[str, int]]], base glyph to layers mapping

287

"""

288

289

def setupCPAL(self, palettes):

290

"""

291

Set up color palettes (CPAL table).

292

293

Parameters:

294

- palettes: List[List[Tuple[float, float, float, float]]], color palettes with RGBA values

295

"""

296

```

297

298

#### Color Font Example

299

300

```python

301

# Create color font with emoji-style glyphs

302

fb = FontBuilder(unitsPerEm=1000, isTTF=True)

303

304

# ... basic setup ...

305

306

# Define color layers

307

color_layers = {

308

'heart': [

309

('heart.base', 0), # Base shape uses palette color 0

310

('heart.highlight', 1) # Highlight uses palette color 1

311

]

312

}

313

fb.setupCOLR(color_layers)

314

315

# Define color palette

316

palettes = [[

317

(1.0, 0.0, 0.0, 1.0), # Red (RGBA)

318

(1.0, 0.5, 0.5, 1.0) # Light red

319

]]

320

fb.setupCPAL(palettes)

321

```

322

323

### Font Validation

324

325

FontBuilder automatically handles many validation requirements, but additional checks can be performed:

326

327

```python

328

# Validate required tables are present

329

font = fb.font

330

required_tables = ['head', 'hhea', 'hmtx', 'maxp', 'name', 'cmap', 'post']

331

332

for table_tag in required_tables:

333

if table_tag not in font:

334

print(f"Warning: Missing required table {table_tag}")

335

336

# Validate glyph metrics consistency

337

if 'hmtx' in font and 'hhea' in font:

338

hmtx = font['hmtx']

339

hhea = font['hhea']

340

if hhea.numberOfHMetrics != len(hmtx.metrics):

341

print("Warning: Inconsistent horizontal metrics count")

342

```

343

344

## Common Font Building Patterns

345

346

### Building from Vector Data

347

348

```python

349

from fontTools.misc.bezierTools import calcBounds

350

351

def build_glyph_from_contours(contours):

352

"""Build glyph from list of contour point lists."""

353

pen = TTGlyphPen(None)

354

355

for contour in contours:

356

if not contour:

357

continue

358

359

pen.moveTo(contour[0])

360

for point in contour[1:]:

361

pen.lineTo(point)

362

pen.closePath()

363

364

return pen.glyph()

365

366

# Calculate bounding box for metrics

367

def calc_glyph_bounds(glyph):

368

"""Calculate bounding box for glyph."""

369

if hasattr(glyph, 'coordinates'):

370

coords = glyph.coordinates

371

if coords:

372

return calcBounds(coords)

373

return (0, 0, 0, 0)

374

```

375

376

### Batch Font Generation

377

378

```python

379

def create_font_family(base_glyphs, family_name, styles):

380

"""Create multiple fonts in a family."""

381

fonts = {}

382

383

for style_name, style_params in styles.items():

384

fb = FontBuilder(unitsPerEm=1000, isTTF=True)

385

386

# Apply style-specific modifications to glyphs

387

styled_glyphs = apply_style_to_glyphs(base_glyphs, style_params)

388

389

# ... setup steps ...

390

391

names = {

392

"familyName": family_name,

393

"styleName": style_name,

394

"fullName": f"{family_name} {style_name}",

395

# ... other names

396

}

397

fb.setupNameTable(names)

398

399

fonts[style_name] = fb.font

400

401

return fonts

402

```

403

404

## Error Handling

405

406

FontBuilder methods may raise exceptions for invalid parameters or missing dependencies:

407

408

- **ValueError**: Invalid parameters or missing required data

409

- **KeyError**: Referenced glyph names not in glyph order

410

- **TypeError**: Incorrect parameter types

411

- **AttributeError**: Missing required attributes in glyph objects

412

413

Always ensure proper setup sequence and validate input data before font building operations.