0
# Utilities and Tools
1
2
Miscellaneous utilities for text processing, geometric transformations, XML handling, and various font-related calculations. These utilities provide foundational support for font manipulation and processing workflows.
3
4
## Capabilities
5
6
### Geometric Transformations
7
8
2D affine transformation matrix operations for scaling, rotation, translation, and skewing.
9
10
```python { .api }
11
class Transform:
12
def __init__(self, a=1, b=0, c=0, d=1, e=0, f=0):
13
"""
14
Create 2D affine transformation matrix.
15
16
Parameters:
17
- a, b, c, d, e, f: float, transformation matrix components
18
[a c e] [x] [a*x + c*y + e]
19
[b d f] × [y] = [b*x + d*y + f]
20
[0 0 1] [1] [1]
21
"""
22
23
def transformPoint(self, point):
24
"""
25
Apply transformation to point.
26
27
Parameters:
28
- point: Tuple[float, float], input coordinates
29
30
Returns:
31
Tuple[float, float]: Transformed coordinates
32
"""
33
34
def transformPoints(self, points):
35
"""
36
Apply transformation to list of points.
37
38
Parameters:
39
- points: List[Tuple[float, float]], input points
40
41
Returns:
42
List[Tuple[float, float]]: Transformed points
43
"""
44
45
def scale(self, x, y=None):
46
"""
47
Create scaling transformation.
48
49
Parameters:
50
- x: float, horizontal scale factor
51
- y: float, vertical scale factor (default: same as x)
52
53
Returns:
54
Transform: New scaling transformation
55
"""
56
57
def rotate(self, angle):
58
"""
59
Create rotation transformation.
60
61
Parameters:
62
- angle: float, rotation angle in radians
63
64
Returns:
65
Transform: New rotation transformation
66
"""
67
68
def translate(self, x, y):
69
"""
70
Create translation transformation.
71
72
Parameters:
73
- x, y: float, translation offsets
74
75
Returns:
76
Transform: New translation transformation
77
"""
78
79
def skew(self, x, y):
80
"""
81
Create skew transformation.
82
83
Parameters:
84
- x, y: float, skew angles in radians
85
86
Returns:
87
Transform: New skew transformation
88
"""
89
90
def inverse(self):
91
"""
92
Get inverse transformation.
93
94
Returns:
95
Transform: Inverse transformation matrix
96
"""
97
98
def __mul__(self, other):
99
"""
100
Multiply transformations.
101
102
Parameters:
103
- other: Transform, transformation to multiply with
104
105
Returns:
106
Transform: Combined transformation
107
"""
108
109
Identity = Transform() # Identity transformation constant
110
```
111
112
#### Transformation Examples
113
114
```python
115
from fontTools.misc.transform import Transform, Identity
116
import math
117
118
# Create basic transformations
119
scale_transform = Transform().scale(2.0, 1.5) # Scale 2x horizontally, 1.5x vertically
120
rotate_transform = Transform().rotate(math.radians(45)) # Rotate 45 degrees
121
translate_transform = Transform().translate(100, 50) # Move 100 right, 50 up
122
123
# Combine transformations
124
combined = scale_transform * rotate_transform * translate_transform
125
126
# Apply to points
127
original_points = [(0, 0), (100, 0), (100, 100), (0, 100)]
128
transformed_points = combined.transformPoints(original_points)
129
130
print("Original:", original_points)
131
print("Transformed:", transformed_points)
132
133
# Create complex transformation
134
complex_transform = (Transform()
135
.translate(-50, -50) # Center at origin
136
.rotate(math.radians(30)) # Rotate 30 degrees
137
.scale(1.2) # Scale up 20%
138
.translate(200, 300)) # Move to final position
139
140
# Apply to single point
141
x, y = complex_transform.transformPoint((100, 100))
142
print(f"Point (100, 100) transformed to ({x}, {y})")
143
```
144
145
### Array and Bounds Utilities
146
147
Functions for working with coordinate arrays and bounding box calculations.
148
149
```python { .api }
150
def calcBounds(array):
151
"""
152
Calculate bounding box of coordinate array.
153
154
Parameters:
155
- array: List[Tuple[float, float]], coordinate points
156
157
Returns:
158
Tuple[float, float, float, float]: (xMin, yMin, xMax, yMax) or None if empty
159
"""
160
161
def updateBounds(bounds, p):
162
"""
163
Update bounding box to include point.
164
165
Parameters:
166
- bounds: Tuple[float, float, float, float], current bounds (xMin, yMin, xMax, yMax)
167
- p: Tuple[float, float], point to include
168
169
Returns:
170
Tuple[float, float, float, float]: Updated bounds
171
"""
172
173
def pointInRect(p, rect):
174
"""
175
Test if point is inside rectangle.
176
177
Parameters:
178
- p: Tuple[float, float], point coordinates
179
- rect: Tuple[float, float, float, float], rectangle (xMin, yMin, xMax, yMax)
180
181
Returns:
182
bool: True if point is inside rectangle
183
"""
184
185
def rectCenter(rect):
186
"""
187
Get center point of rectangle.
188
189
Parameters:
190
- rect: Tuple[float, float, float, float], rectangle (xMin, yMin, xMax, yMax)
191
192
Returns:
193
Tuple[float, float]: Center coordinates
194
"""
195
196
def intRect(rect):
197
"""
198
Convert rectangle to integer coordinates.
199
200
Parameters:
201
- rect: Tuple[float, float, float, float], rectangle with float coordinates
202
203
Returns:
204
Tuple[int, int, int, int]: Rectangle with integer coordinates
205
"""
206
```
207
208
#### Array and Bounds Examples
209
210
```python
211
from fontTools.misc.arrayTools import calcBounds, updateBounds, rectCenter
212
213
# Calculate bounds of point array
214
points = [(10, 20), (50, 10), (30, 60), (80, 40)]
215
bounds = calcBounds(points)
216
print(f"Bounds: {bounds}") # (10, 10, 80, 60)
217
218
# Update bounds incrementally
219
current_bounds = None
220
for point in points:
221
if current_bounds is None:
222
current_bounds = (point[0], point[1], point[0], point[1])
223
else:
224
current_bounds = updateBounds(current_bounds, point)
225
226
print(f"Incremental bounds: {current_bounds}")
227
228
# Get rectangle center
229
center = rectCenter(bounds)
230
print(f"Center: {center}") # (45.0, 35.0)
231
```
232
233
### Text Processing Utilities
234
235
Functions for handling hexadecimal data, safe evaluation, and text conversion.
236
237
```python { .api }
238
def safeEval(data):
239
"""
240
Safely evaluate simple Python expressions.
241
242
Parameters:
243
- data: str, Python expression to evaluate
244
245
Returns:
246
Any: Evaluation result
247
"""
248
249
def readHex(content):
250
"""
251
Read hexadecimal string and return bytes.
252
253
Parameters:
254
- content: str, hexadecimal string
255
256
Returns:
257
bytes: Decoded binary data
258
"""
259
260
def writeHex(data):
261
"""
262
Convert bytes to hexadecimal string.
263
264
Parameters:
265
- data: bytes, binary data
266
267
Returns:
268
str: Hexadecimal representation
269
"""
270
271
def hexStr(value):
272
"""
273
Convert integer to hexadecimal string.
274
275
Parameters:
276
- value: int, integer value
277
278
Returns:
279
str: Hexadecimal string (e.g., "0x1a2b")
280
"""
281
```
282
283
#### Text Processing Examples
284
285
```python
286
from fontTools.misc.textTools import safeEval, readHex, writeHex, hexStr
287
288
# Safe evaluation
289
result = safeEval("2 + 3 * 4")
290
print(f"Safe eval result: {result}") # 14
291
292
# Hex conversion
293
hex_string = "48656c6c6f20576f726c64" # "Hello World" in hex
294
binary_data = readHex(hex_string)
295
print(f"Decoded: {binary_data}") # b'Hello World'
296
297
# Convert back to hex
298
hex_result = writeHex(binary_data)
299
print(f"Re-encoded: {hex_result}")
300
301
# Integer to hex
302
print(f"0x{255:02x}") # 0xff
303
print(hexStr(255)) # 0xff
304
```
305
306
### Rounding Utilities
307
308
Functions for various rounding operations commonly used in font processing.
309
310
```python { .api }
311
def roundFunc(value, func=round):
312
"""
313
Apply rounding function safely.
314
315
Parameters:
316
- value: float, value to round
317
- func: callable, rounding function (default: round)
318
319
Returns:
320
int: Rounded value
321
"""
322
323
def noRound(value):
324
"""
325
No-op rounding function (returns input unchanged).
326
327
Parameters:
328
- value: float, input value
329
330
Returns:
331
float: Unchanged input value
332
"""
333
334
def otRound(value):
335
"""
336
OpenType-style rounding (rounds 0.5 up).
337
338
Parameters:
339
- value: float, value to round
340
341
Returns:
342
int: Rounded value using OpenType rules
343
"""
344
```
345
346
#### Rounding Examples
347
348
```python
349
from fontTools.misc.roundTools import roundFunc, noRound, otRound
350
351
values = [1.2, 1.5, 1.7, 2.5, -1.5]
352
353
print("Standard rounding:")
354
for val in values:
355
print(f" {val} -> {roundFunc(val)}")
356
357
print("OpenType rounding:")
358
for val in values:
359
print(f" {val} -> {otRound(val)}")
360
361
print("No rounding:")
362
for val in values:
363
print(f" {val} -> {noRound(val)}")
364
```
365
366
### Fixed-Point Arithmetic
367
368
Utilities for working with fixed-point numbers used in font tables.
369
370
```python { .api }
371
def fixedToFloat(value, precisionBits=16):
372
"""
373
Convert fixed-point integer to floating-point.
374
375
Parameters:
376
- value: int, fixed-point value
377
- precisionBits: int, number of fractional bits (default: 16)
378
379
Returns:
380
float: Floating-point representation
381
"""
382
383
def floatToFixed(value, precisionBits=16):
384
"""
385
Convert floating-point to fixed-point integer.
386
387
Parameters:
388
- value: float, floating-point value
389
- precisionBits: int, number of fractional bits (default: 16)
390
391
Returns:
392
int: Fixed-point representation
393
"""
394
395
def ensureVersionIsLong(value):
396
"""
397
Ensure version number is in correct format.
398
399
Parameters:
400
- value: int or float, version number
401
402
Returns:
403
int: Properly formatted version number
404
"""
405
```
406
407
#### Fixed-Point Examples
408
409
```python
410
from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
411
412
# Convert between fixed-point and float
413
float_val = 1.5
414
fixed_val = floatToFixed(float_val)
415
print(f"Float {float_val} -> Fixed {fixed_val}") # 98304 (1.5 * 65536)
416
417
back_to_float = fixedToFloat(fixed_val)
418
print(f"Fixed {fixed_val} -> Float {back_to_float}") # 1.5
419
420
# Different precision
421
fixed_8bit = floatToFixed(1.5, precisionBits=8)
422
print(f"8-bit fixed: {fixed_8bit}") # 384 (1.5 * 256)
423
```
424
425
### Bezier Curve Mathematics
426
427
Mathematical functions for working with Bezier curves and path calculations.
428
429
```python { .api }
430
def calcQuadraticBounds(pt1, pt2, pt3):
431
"""
432
Calculate bounding box of quadratic Bezier curve.
433
434
Parameters:
435
- pt1, pt2, pt3: Tuple[float, float], curve control points
436
437
Returns:
438
Tuple[float, float, float, float]: Bounding box (xMin, yMin, xMax, yMax)
439
"""
440
441
def calcCubicBounds(pt1, pt2, pt3, pt4):
442
"""
443
Calculate bounding box of cubic Bezier curve.
444
445
Parameters:
446
- pt1, pt2, pt3, pt4: Tuple[float, float], curve control points
447
448
Returns:
449
Tuple[float, float, float, float]: Bounding box (xMin, yMin, xMax, yMax)
450
"""
451
452
def splitLine(pt1, pt2, where):
453
"""
454
Split line segment at parameter t.
455
456
Parameters:
457
- pt1, pt2: Tuple[float, float], line endpoints
458
- where: float, parameter t (0.0 to 1.0)
459
460
Returns:
461
Tuple[Tuple, Tuple]: Two line segments
462
"""
463
464
def splitQuadratic(pt1, pt2, pt3, where):
465
"""
466
Split quadratic Bezier curve at parameter t.
467
468
Parameters:
469
- pt1, pt2, pt3: Tuple[float, float], curve control points
470
- where: float, parameter t (0.0 to 1.0)
471
472
Returns:
473
Tuple[Tuple, Tuple]: Two quadratic curve segments
474
"""
475
476
def splitCubic(pt1, pt2, pt3, pt4, where):
477
"""
478
Split cubic Bezier curve at parameter t.
479
480
Parameters:
481
- pt1, pt2, pt3, pt4: Tuple[float, float], curve control points
482
- where: float, parameter t (0.0 to 1.0)
483
484
Returns:
485
Tuple[Tuple, Tuple]: Two cubic curve segments
486
"""
487
```
488
489
#### Bezier Mathematics Examples
490
491
```python
492
from fontTools.misc.bezierTools import calcCubicBounds, splitCubic
493
494
# Calculate bounds of cubic curve
495
pt1 = (0, 0)
496
pt2 = (50, 100)
497
pt3 = (150, 100)
498
pt4 = (200, 0)
499
500
bounds = calcCubicBounds(pt1, pt2, pt3, pt4)
501
print(f"Curve bounds: {bounds}")
502
503
# Split curve at midpoint
504
left_curve, right_curve = splitCubic(pt1, pt2, pt3, pt4, 0.5)
505
print(f"Left curve: {left_curve}")
506
print(f"Right curve: {right_curve}")
507
```
508
509
### XML Processing
510
511
Utilities for reading and writing XML data in font processing contexts.
512
513
```python { .api }
514
class XMLWriter:
515
def __init__(self, outFile, encoding="utf-8"):
516
"""
517
XML output writer.
518
519
Parameters:
520
- outFile: file-like, output file object
521
- encoding: str, character encoding
522
"""
523
524
def begintag(self, tag, **kwargs):
525
"""
526
Write opening XML tag.
527
528
Parameters:
529
- tag: str, tag name
530
- kwargs: tag attributes
531
"""
532
533
def endtag(self, tag):
534
"""
535
Write closing XML tag.
536
537
Parameters:
538
- tag: str, tag name
539
"""
540
541
def simpletag(self, tag, **kwargs):
542
"""
543
Write self-closing XML tag.
544
545
Parameters:
546
- tag: str, tag name
547
- kwargs: tag attributes
548
"""
549
550
def data(self, data):
551
"""
552
Write XML character data.
553
554
Parameters:
555
- data: str, character data
556
"""
557
558
class XMLReader:
559
def __init__(self, inFile):
560
"""
561
XML input reader.
562
563
Parameters:
564
- inFile: file-like, input file object
565
"""
566
567
def read(self, handler):
568
"""
569
Parse XML with content handler.
570
571
Parameters:
572
- handler: object, content handler with start/end/data methods
573
"""
574
```
575
576
### Logging Configuration
577
578
Utilities for configuring FontTools logging output.
579
580
```python { .api }
581
def configLogger(level="INFO", format=None, logger=None):
582
"""
583
Configure FontTools logging.
584
585
Parameters:
586
- level: str, logging level ("DEBUG", "INFO", "WARNING", "ERROR")
587
- format: str, log message format string
588
- logger: Logger, specific logger to configure (default: fontTools logger)
589
"""
590
```
591
592
#### Logging Examples
593
594
```python
595
from fontTools.misc.loggingTools import configLogger
596
import logging
597
598
# Configure FontTools logging
599
configLogger(level="DEBUG")
600
601
# Use FontTools logger
602
from fontTools import log
603
log.info("Processing font file")
604
log.warning("Unusual glyph structure detected")
605
log.error("Font validation failed")
606
607
# Configure with custom format
608
configLogger(
609
level="INFO",
610
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
611
)
612
```
613
614
### File System Utilities
615
616
Cross-platform file system operations for font processing.
617
618
```python
619
from fontTools.misc.filesystem import open_file, ensure_path
620
621
# Platform-aware file opening
622
with open_file("font.ttf", "rb") as f:
623
font_data = f.read()
624
625
# Ensure directory exists
626
ensure_path("output/fonts/")
627
```
628
629
### Integration Examples
630
631
```python
632
def optimize_glyph_coordinates(glyph_coords, transform=None):
633
"""Example combining multiple utilities."""
634
from fontTools.misc.arrayTools import calcBounds
635
from fontTools.misc.roundTools import otRound
636
from fontTools.misc.transform import Transform
637
638
# Apply transformation if provided
639
if transform:
640
coords = transform.transformPoints(glyph_coords)
641
else:
642
coords = glyph_coords
643
644
# Round coordinates using OpenType rules
645
rounded_coords = [(otRound(x), otRound(y)) for x, y in coords]
646
647
# Calculate optimized bounds
648
bounds = calcBounds(rounded_coords)
649
650
return rounded_coords, bounds
651
652
def create_web_font_transform(target_upm=1000):
653
"""Create transformation for web font optimization."""
654
from fontTools.misc.transform import Transform
655
656
# Common web font optimizations
657
transform = Transform()
658
659
# Scale to target units per em
660
current_upm = 2048 # Assume source UPM
661
scale_factor = target_upm / current_upm
662
transform = transform.scale(scale_factor)
663
664
# Optional: slight compression for better rendering
665
transform = transform.scale(1.0, 0.95)
666
667
return transform
668
```