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-processing.mddocs/

0

# Font Processing

1

2

Font optimization, subsetting, and merging tools for reducing file sizes, removing unused features, and combining multiple fonts into collections. These tools are essential for web font optimization and font distribution workflows.

3

4

## Capabilities

5

6

### Font Subsetting

7

8

Remove unused glyphs, features, and tables to create optimized fonts with smaller file sizes.

9

10

```python { .api }

11

class Subsetter:

12

def __init__(self, options=None):

13

"""

14

Initialize font subsetter.

15

16

Parameters:

17

- options: Options, subsetting configuration (default: default options)

18

"""

19

20

def subset(self, font):

21

"""

22

Apply subsetting to TTFont instance.

23

24

Parameters:

25

- font: TTFont, font to subset (modified in-place)

26

"""

27

28

class Options:

29

def __init__(self, **kwargs):

30

"""

31

Subsetting configuration options.

32

33

Parameters:

34

- text: str, characters to keep (default: None)

35

- unicodes: List[int], Unicode codepoints to keep

36

- glyphs: List[str], glyph names to keep

37

- layout_features: List[str], OpenType features to keep

38

- name_IDs: List[int], name table entries to keep

39

- name_languages: List[int], name table languages to keep

40

- drop_tables: List[str], tables to remove

41

- passthrough_tables: bool, keep unhandled tables

42

- hinting: bool, keep hinting information

43

- legacy_kern: bool, keep legacy kern table

44

- symbol_cmap: bool, keep symbol character maps

45

- ignore_missing_glyphs: bool, continue if glyphs missing

46

- ignore_missing_unicodes: bool, continue if Unicode missing

47

"""

48

```

49

50

#### Basic Subsetting Example

51

52

```python

53

from fontTools.subset import Subsetter, Options

54

from fontTools.ttLib import TTFont

55

56

# Load font

57

font = TTFont("input.ttf")

58

59

# Create subsetter with options

60

options = Options(

61

text="Hello World!", # Keep only these characters

62

layout_features=['kern', 'liga'], # Keep kerning and ligatures

63

hinting=True, # Preserve hinting

64

ignore_missing_glyphs=True # Don't fail on missing glyphs

65

)

66

67

subsetter = Subsetter(options)

68

subsetter.subset(font)

69

70

# Save subsetted font

71

font.save("output_subset.ttf")

72

font.close()

73

```

74

75

#### Advanced Subsetting

76

77

```python

78

# Subset by Unicode ranges

79

options = Options(

80

unicodes=list(range(0x20, 0x7F)) + list(range(0xA0, 0xFF)), # Basic Latin + Latin-1

81

layout_features=['kern', 'liga', 'clig', 'frac'],

82

drop_tables=['DSIG', 'GSUB', 'GPOS'], # Remove specific tables

83

name_IDs=[1, 2, 3, 4, 5, 6], # Keep essential name entries

84

hinting=False # Remove hinting for smaller size

85

)

86

87

subsetter = Subsetter(options)

88

subsetter.subset(font)

89

```

90

91

#### Web Font Optimization

92

93

```python

94

def optimize_for_web(input_path, output_path, text_content):

95

"""Optimize font for web usage."""

96

font = TTFont(input_path)

97

98

# Web-optimized subsetting

99

options = Options(

100

text=text_content,

101

layout_features=['kern', 'liga'], # Common web features

102

drop_tables=['DSIG', 'prep', 'fpgm', 'cvt '], # Remove hinting tables

103

hinting=False, # Remove hinting

104

desubroutinize=True, # Flatten CFF subroutines

105

passthrough_tables=False # Drop unknown tables

106

)

107

108

subsetter = Subsetter(options)

109

subsetter.subset(font)

110

111

font.save(output_path)

112

font.close()

113

114

print(f"Optimized font saved to {output_path}")

115

```

116

117

### Font Merging

118

119

Combine multiple fonts into a single font or font collection.

120

121

```python { .api }

122

class Merger:

123

def __init__(self, options=None):

124

"""

125

Initialize font merger.

126

127

Parameters:

128

- options: Options, merging configuration

129

"""

130

131

def merge(self, fontfiles):

132

"""

133

Merge multiple fonts into single font.

134

135

Parameters:

136

- fontfiles: List[str], paths to font files to merge

137

138

Returns:

139

TTFont: Merged font

140

"""

141

142

class Options:

143

def __init__(self, **kwargs):

144

"""

145

Font merging options.

146

147

Parameters:

148

- verbose: bool, enable verbose output

149

- timing: bool, report timing information

150

- ignore_missing_glyphs: bool, continue on missing glyphs

151

- drop_tables: List[str], tables to exclude from merge

152

"""

153

```

154

155

#### Basic Font Merging

156

157

```python

158

from fontTools.merge import Merger, Options

159

from fontTools.ttLib import TTFont

160

161

# Merge multiple fonts

162

merger = Merger()

163

merged_font = merger.merge(["font1.ttf", "font2.ttf", "font3.ttf"])

164

165

# Save merged result

166

merged_font.save("merged_font.ttf")

167

merged_font.close()

168

```

169

170

#### Advanced Merging with Options

171

172

```python

173

# Configure merging options

174

options = Options(

175

verbose=True,

176

ignore_missing_glyphs=True,

177

drop_tables=['DSIG'] # Drop digital signatures

178

)

179

180

merger = Merger(options)

181

182

# Merge fonts with specific character ranges

183

latin_font = TTFont("latin.ttf")

184

cyrillic_font = TTFont("cyrillic.ttf")

185

arabic_font = TTFont("arabic.ttf")

186

187

merged_font = merger.merge([latin_font, cyrillic_font, arabic_font])

188

merged_font.save("multilingual.ttf")

189

```

190

191

### TTX XML Conversion

192

193

Convert fonts to/from TTX XML format for human-readable font analysis and modification.

194

195

```python { .api }

196

def main(args=None):

197

"""

198

Convert OpenType fonts to XML and back.

199

200

Parameters:

201

- args: List[str], command line arguments

202

Common options:

203

- -d: dump font to XML

204

- -m: merge XML back to font

205

- -t: specify tables to dump/compile

206

- -x: exclude specific tables

207

- -s: split tables into separate files

208

- -i: don't disassemble TT instructions

209

- -z: use WOFF2 compression

210

"""

211

```

212

213

#### TTX Usage Examples

214

215

```python

216

import subprocess

217

218

# Convert font to XML

219

subprocess.run(["fonttools", "ttx", "font.ttf"]) # Creates font.ttx

220

221

# Convert specific tables only

222

subprocess.run(["fonttools", "ttx", "-t", "cmap", "-t", "name", "font.ttf"])

223

224

# Convert XML back to font

225

subprocess.run(["fonttools", "ttx", "font.ttx"]) # Creates font.ttf

226

227

# Split each table into separate XML file

228

subprocess.run(["fonttools", "ttx", "-s", "font.ttf"])

229

```

230

231

### Command-Line Interface

232

233

FontTools provides command-line tools for batch processing:

234

235

```python { .api }

236

def main(args=None):

237

"""

238

Font subsetting command-line interface.

239

240

Parameters:

241

- args: List[str], command line arguments

242

"""

243

```

244

245

#### Command-Line Usage

246

247

```bash

248

# Subset font to specific text

249

fonttools subset font.ttf --text="Hello World"

250

251

# Subset with Unicode ranges

252

fonttools subset font.ttf --unicodes="U+0020-007F,U+00A0-00FF"

253

254

# Web font optimization

255

fonttools subset font.ttf --text-file=content.txt --layout-features="kern,liga" --no-hinting

256

257

# Merge fonts

258

fonttools merge font1.ttf font2.ttf font3.ttf

259

260

# TTX conversion

261

fonttools ttx font.ttf # to XML

262

fonttools ttx font.ttx # to binary

263

```

264

265

### Error Handling

266

267

```python { .api }

268

class SubsettingError(Exception):

269

"""Base subsetting exception."""

270

271

class MissingGlyphsSubsettingError(SubsettingError):

272

"""Required glyphs are missing from font."""

273

274

class MissingUnicodesSubsettingError(SubsettingError):

275

"""Required Unicode codepoints are missing from font."""

276

```

277

278

### Batch Processing Utilities

279

280

```python

281

def process_font_directory(input_dir, output_dir, text_content):

282

"""Process all fonts in a directory."""

283

import os

284

from pathlib import Path

285

286

input_path = Path(input_dir)

287

output_path = Path(output_dir)

288

output_path.mkdir(exist_ok=True)

289

290

for font_file in input_path.glob("*.ttf"):

291

try:

292

font = TTFont(font_file)

293

294

# Apply subsetting

295

options = Options(text=text_content, hinting=False)

296

subsetter = Subsetter(options)

297

subsetter.subset(font)

298

299

# Save with suffix

300

output_file = output_path / f"{font_file.stem}_subset.ttf"

301

font.save(output_file)

302

font.close()

303

304

print(f"Processed: {font_file.name} -> {output_file.name}")

305

306

except Exception as e:

307

print(f"Error processing {font_file.name}: {e}")

308

309

# Usage

310

process_font_directory("input_fonts/", "output_fonts/", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

311

```

312

313

### Performance Optimization

314

315

```python

316

def optimize_subsetting_performance(font_paths, common_options):

317

"""Optimize subsetting for multiple fonts with shared options."""

318

# Reuse subsetter instance for better performance

319

subsetter = Subsetter(common_options)

320

321

for font_path in font_paths:

322

font = TTFont(font_path)

323

324

# Create a copy for subsetting (preserves original)

325

font_copy = TTFont()

326

font_copy.importXML(font.saveXML())

327

328

subsetter.subset(font_copy)

329

330

output_path = font_path.replace('.ttf', '_subset.ttf')

331

font_copy.save(output_path)

332

333

font.close()

334

font_copy.close()

335

```

336

337

### Integration Patterns

338

339

```python

340

def create_web_font_variants(font_path, text_samples):

341

"""Create multiple optimized variants for different use cases."""

342

base_font = TTFont(font_path)

343

variants = {}

344

345

# Full feature set (headlines)

346

headline_options = Options(

347

text=text_samples['headlines'],

348

layout_features=['kern', 'liga', 'clig', 'dlig'],

349

hinting=True

350

)

351

variants['headline'] = create_subset(base_font, headline_options)

352

353

# Basic features (body text)

354

body_options = Options(

355

text=text_samples['body'],

356

layout_features=['kern'],

357

hinting=False # Smaller size for body text

358

)

359

variants['body'] = create_subset(base_font, body_options)

360

361

# Minimal (fallback)

362

fallback_options = Options(

363

text=text_samples['essential'],

364

layout_features=[],

365

hinting=False

366

)

367

variants['fallback'] = create_subset(base_font, fallback_options)

368

369

return variants

370

371

def create_subset(base_font, options):

372

"""Helper to create font subset."""

373

# Create deep copy to avoid modifying original

374

font = TTFont()

375

font.importXML(base_font.saveXML())

376

377

subsetter = Subsetter(options)

378

subsetter.subset(font)

379

380

return font

381

```