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

variable-fonts.mddocs/

0

# Variable Fonts

1

2

Build and manipulate variable fonts using design space documents, supporting multi-axis font variations and instance generation. Variable fonts allow a single font file to contain multiple styles and weights along continuous axes.

3

4

## Capabilities

5

6

### Variable Font Building

7

8

Core functions for building variable fonts from master sources and design space documents.

9

10

```python { .api }

11

def build(designspace, master_finder=lambda s: s, exclude=[], optimize=True, **kwargs):

12

"""

13

Build variable font from designspace document.

14

15

Parameters:

16

- designspace: str or DesignSpaceDocument, path to .designspace file or document object

17

- master_finder: callable, function to locate master font files

18

- exclude: List[str], OpenType tables to exclude from variation

19

- optimize: bool, enable variation optimization

20

- kwargs: additional build options

21

22

Returns:

23

TTFont: Variable font with variation tables

24

"""

25

26

def build_many(designspace, master_finder=lambda s: s, exclude=[], **kwargs):

27

"""

28

Build multiple variable fonts from designspace v5 document.

29

30

Parameters:

31

- designspace: str or DesignSpaceDocument, designspace file or document

32

- master_finder: callable, function to locate master font files

33

- exclude: List[str], tables to exclude

34

- kwargs: additional build options

35

36

Returns:

37

List[TTFont]: List of built variable fonts

38

"""

39

40

def load_designspace(designspace):

41

"""

42

Parse and validate designspace document.

43

44

Parameters:

45

- designspace: str, path to designspace file

46

47

Returns:

48

DesignSpaceDocument: Parsed and validated document

49

"""

50

51

def load_masters(designspace, master_finder):

52

"""

53

Load master fonts referenced in designspace.

54

55

Parameters:

56

- designspace: DesignSpaceDocument, design space document

57

- master_finder: callable, function to resolve master font paths

58

59

Returns:

60

Dict[str, TTFont]: Master fonts keyed by source identifier

61

"""

62

```

63

64

#### Basic Variable Font Building

65

66

```python

67

from fontTools.varLib import build

68

from fontTools.designspaceLib import DesignSpaceDocument

69

70

# Build from designspace file

71

variable_font = build("MyFont.designspace")

72

variable_font.save("MyFont-VF.ttf")

73

74

# Build with custom master finder

75

def find_masters(source_path):

76

"""Custom logic to locate master fonts."""

77

return f"masters/{source_path}"

78

79

variable_font = build("MyFont.designspace", master_finder=find_masters)

80

```

81

82

#### Advanced Building with Options

83

84

```python

85

# Build with optimization and exclusions

86

variable_font = build(

87

"MyFont.designspace",

88

exclude=['DSIG', 'hdmx'], # Exclude problematic tables

89

optimize=True, # Enable delta optimization

90

overlap_tolerance=0.5, # Overlap detection tolerance

91

cff_version=2 # Use CFF2 for PostScript outlines

92

)

93

94

# Build multiple fonts from v5 designspace

95

fonts = build_many("MyFamily.designspace")

96

for i, font in enumerate(fonts):

97

font.save(f"MyFamily-VF-{i}.ttf")

98

```

99

100

### Design Space Documents

101

102

Classes for creating and manipulating design space documents that define variable font parameters.

103

104

```python { .api }

105

class DesignSpaceDocument:

106

def __init__(self):

107

"""Initialize empty design space document."""

108

109

def read(self, path):

110

"""

111

Read designspace from file.

112

113

Parameters:

114

- path: str, path to .designspace file

115

"""

116

117

def write(self, path):

118

"""

119

Write designspace to file.

120

121

Parameters:

122

- path: str, output file path

123

"""

124

125

def addAxisDescriptor(self, descriptor):

126

"""

127

Add variation axis definition.

128

129

Parameters:

130

- descriptor: AxisDescriptor, axis definition

131

"""

132

133

def addSourceDescriptor(self, descriptor):

134

"""

135

Add master font source.

136

137

Parameters:

138

- descriptor: SourceDescriptor, source definition

139

"""

140

141

def addInstanceDescriptor(self, descriptor):

142

"""

143

Add named instance definition.

144

145

Parameters:

146

- descriptor: InstanceDescriptor, instance definition

147

"""

148

149

def addRuleDescriptor(self, descriptor):

150

"""

151

Add conditional substitution rule.

152

153

Parameters:

154

- descriptor: RuleDescriptor, rule definition

155

"""

156

```

157

158

#### Creating Design Space Documents

159

160

```python

161

from fontTools.designspaceLib import (

162

DesignSpaceDocument, AxisDescriptor, SourceDescriptor, InstanceDescriptor

163

)

164

165

# Create new design space

166

doc = DesignSpaceDocument()

167

168

# Define weight axis

169

weight_axis = AxisDescriptor()

170

weight_axis.name = "Weight"

171

weight_axis.tag = "wght"

172

weight_axis.minimum = 100

173

weight_axis.default = 400

174

weight_axis.maximum = 900

175

doc.addAxisDescriptor(weight_axis)

176

177

# Define width axis

178

width_axis = AxisDescriptor()

179

width_axis.name = "Width"

180

width_axis.tag = "wdth"

181

width_axis.minimum = 75

182

width_axis.default = 100

183

width_axis.maximum = 125

184

doc.addAxisDescriptor(width_axis)

185

186

# Add master sources

187

light_source = SourceDescriptor()

188

light_source.filename = "MyFont-Light.ufo"

189

light_source.location = {"Weight": 100, "Width": 100}

190

doc.addSourceDescriptor(light_source)

191

192

regular_source = SourceDescriptor()

193

regular_source.filename = "MyFont-Regular.ufo"

194

regular_source.location = {"Weight": 400, "Width": 100}

195

regular_source.copyInfo = True # Copy font info from this master

196

doc.addSourceDescriptor(regular_source)

197

198

bold_source = SourceDescriptor()

199

bold_source.filename = "MyFont-Bold.ufo"

200

bold_source.location = {"Weight": 900, "Width": 100}

201

doc.addSourceDescriptor(bold_source)

202

203

# Add named instances

204

regular_instance = InstanceDescriptor()

205

regular_instance.name = "Regular"

206

regular_instance.location = {"Weight": 400, "Width": 100}

207

doc.addInstanceDescriptor(regular_instance)

208

209

bold_instance = InstanceDescriptor()

210

bold_instance.name = "Bold"

211

bold_instance.location = {"Weight": 700, "Width": 100}

212

doc.addInstanceDescriptor(bold_instance)

213

214

# Save design space

215

doc.write("MyFont.designspace")

216

```

217

218

### Descriptor Classes

219

220

Classes that define the components of a design space document.

221

222

```python { .api }

223

class AxisDescriptor:

224

def __init__(self):

225

"""Initialize axis descriptor."""

226

227

name: str # Human-readable axis name

228

tag: str # 4-character axis tag

229

minimum: float # Minimum axis value

230

default: float # Default axis value

231

maximum: float # Maximum axis value

232

hidden: bool # Hide axis from user interfaces

233

labelNames: Dict # Localized axis names

234

235

class SourceDescriptor:

236

def __init__(self):

237

"""Initialize source descriptor."""

238

239

filename: str # Path to master font file

240

location: Dict # Axis coordinates for this master

241

copyInfo: bool # Copy font info from this master

242

copyGroups: bool # Copy glyph groups

243

copyLib: bool # Copy font lib

244

copyFeatures: bool # Copy OpenType features

245

muteKerning: bool # Disable kerning from this master

246

mutedGlyphNames: List # Glyphs to exclude from this master

247

248

class InstanceDescriptor:

249

def __init__(self):

250

"""Initialize instance descriptor."""

251

252

name: str # Instance name

253

location: Dict # Axis coordinates

254

styleName: str # Style name for naming table

255

familyName: str # Family name override

256

postScriptFontName: str # PostScript name

257

styleMapFamilyName: str # Style map family name

258

styleMapStyleName: str # Style map style name

259

260

class RuleDescriptor:

261

def __init__(self):

262

"""Initialize rule descriptor."""

263

264

name: str # Rule name

265

conditionSets: List[Dict] # Conditions when rule applies

266

subs: List[Tuple[str, str]] # Glyph substitutions (from, to)

267

```

268

269

### Variable Font Utilities

270

271

Utility functions for variable font manipulation and optimization.

272

273

```python { .api }

274

def set_default_weight_width_slant(font, axes_config=None):

275

"""

276

Set default axis values in STAT table.

277

278

Parameters:

279

- font: TTFont, variable font

280

- axes_config: Dict, axis configuration overrides

281

"""

282

283

def drop_implied_oncurve_points(font):

284

"""

285

Optimize TrueType curves by dropping implied on-curve points.

286

287

Parameters:

288

- font: TTFont, font to optimize

289

"""

290

291

def addGSUBFeatureVariations(font, conditional_substitutions):

292

"""

293

Add GSUB feature variations for conditional substitutions.

294

295

Parameters:

296

- font: TTFont, variable font

297

- conditional_substitutions: Dict, feature variation definitions

298

"""

299

```

300

301

#### Variable Font Optimization

302

303

```python

304

from fontTools.varLib import set_default_weight_width_slant, drop_implied_oncurve_points

305

306

# Optimize variable font

307

variable_font = build("MyFont.designspace")

308

309

# Set default axes values for better browser support

310

set_default_weight_width_slant(variable_font, {

311

'wght': 400, # Regular weight as default

312

'wdth': 100 # Normal width as default

313

})

314

315

# Optimize TrueType curves

316

drop_implied_oncurve_points(variable_font)

317

318

variable_font.save("MyFont-VF-Optimized.ttf")

319

```

320

321

### Master Font Finding

322

323

Utilities for locating and validating master font files.

324

325

```python { .api }

326

class MasterFinder:

327

def __init__(self, template):

328

"""

329

Template-based master font finder.

330

331

Parameters:

332

- template: str, path template with placeholders

333

"""

334

335

def __call__(self, source_path):

336

"""

337

Find master font file.

338

339

Parameters:

340

- source_path: str, source file path from designspace

341

342

Returns:

343

str: Resolved master font path

344

"""

345

```

346

347

#### Custom Master Finding

348

349

```python

350

from fontTools.varLib import MasterFinder

351

352

# Template-based finder

353

finder = MasterFinder("masters/{stem}.ufo")

354

355

# Custom finder function

356

def custom_finder(source_path):

357

"""Custom logic for finding masters."""

358

import os

359

360

# Look in multiple directories

361

search_paths = ["masters/", "sources/", "fonts/"]

362

363

for search_dir in search_paths:

364

full_path = os.path.join(search_dir, source_path)

365

if os.path.exists(full_path):

366

return full_path

367

368

raise FileNotFoundError(f"Master not found: {source_path}")

369

370

# Use with build

371

variable_font = build("MyFont.designspace", master_finder=custom_finder)

372

```

373

374

### Feature Variations

375

376

Support for conditional OpenType feature substitutions in variable fonts.

377

378

```python

379

# Add feature variations for different weights

380

feature_variations = {

381

'liga': [

382

{

383

'conditions': [{'wght': (400, 900)}], # Bold weights

384

'substitutions': [('f_i', 'f_i.bold')] # Use bold ligature

385

}

386

]

387

}

388

389

addGSUBFeatureVariations(variable_font, feature_variations)

390

```

391

392

### Error Handling

393

394

Variable font building may raise various exceptions:

395

396

- **FileNotFoundError**: Master font files not found

397

- **DesignSpaceDocumentError**: Invalid designspace document

398

- **VarLibError**: Variable font building errors

399

- **InterpolationError**: Incompatible masters for interpolation

400

401

### Integration Examples

402

403

```python

404

def build_font_family_variable(designspace_path, output_dir):

405

"""Build variable font family with proper naming."""

406

import os

407

from pathlib import Path

408

409

# Load and validate designspace

410

doc = load_designspace(designspace_path)

411

412

# Build variable font

413

vf = build(doc, optimize=True)

414

415

# Set up proper naming

416

name_table = vf['name']

417

family_name = doc.default.familyName or "Variable Font"

418

419

# Update name table entries

420

name_table.setName(family_name, 1, 3, 1, 0x409) # Family name

421

name_table.setName("Variable", 2, 3, 1, 0x409) # Subfamily name

422

423

# Save with proper filename

424

output_path = Path(output_dir) / f"{family_name.replace(' ', '')}-VF.ttf"

425

vf.save(output_path)

426

427

return output_path

428

429

def validate_masters_compatibility(designspace_path):

430

"""Validate that masters are compatible for interpolation."""

431

doc = load_designspace(designspace_path)

432

masters = load_masters(doc, lambda s: s)

433

434

# Check glyph compatibility

435

reference_glyphs = None

436

for source, font in masters.items():

437

glyph_set = set(font.getGlyphOrder())

438

439

if reference_glyphs is None:

440

reference_glyphs = glyph_set

441

elif glyph_set != reference_glyphs:

442

missing = reference_glyphs - glyph_set

443

extra = glyph_set - reference_glyphs

444

print(f"Master {source} compatibility issues:")

445

if missing:

446

print(f" Missing glyphs: {missing}")

447

if extra:

448

print(f" Extra glyphs: {extra}")

449

450

return len(masters)

451

```