Aluode/PerceptionLabPortable
0
1#
2# The Python Imaging Library
3# $Id$
4#
5# base class for raster font file parsers
6#
7# history:
8# 1997-06-05 fl created
9# 1997-08-19 fl restrict image width
10#
11# Copyright (c) 1997-1998 by Secret Labs AB
12# Copyright (c) 1997-1998 by Fredrik Lundh
13#
14# See the README file for information on usage and redistribution.
15#
16from __future__ import annotations
17
18import os
19from typing import BinaryIO
20
21from . import Image, _binary
22
23WIDTH = 800
24
25
26def puti16(
27 fp: BinaryIO, values: tuple[int, int, int, int, int, int, int, int, int, int]
28) -> None:
29 """Write network order (big-endian) 16-bit sequence"""
30 for v in values:
31 if v < 0:
32 v += 65536
33 fp.write(_binary.o16be(v))
34
35
36class FontFile:
37 """Base class for raster font file handlers."""
38
39 bitmap: Image.Image | None = None
40
41 def __init__(self) -> None:
42 self.info: dict[bytes, bytes | int] = {}
43 self.glyph: list[
44 tuple[
45 tuple[int, int],
46 tuple[int, int, int, int],
47 tuple[int, int, int, int],
48 Image.Image,
49 ]
50 | None
51 ] = [None] * 256
52
53 def __getitem__(self, ix: int) -> (
54 tuple[
55 tuple[int, int],
56 tuple[int, int, int, int],
57 tuple[int, int, int, int],
58 Image.Image,
59 ]
60 | None
61 ):
62 return self.glyph[ix]
63
64 def compile(self) -> None:
65 """Create metrics and bitmap"""
66
67 if self.bitmap:
68 return
69
70 # create bitmap large enough to hold all data
71 h = w = maxwidth = 0
72 lines = 1
73 for glyph in self.glyph:
74 if glyph:
75 d, dst, src, im = glyph
76 h = max(h, src[3] - src[1])
77 w = w + (src[2] - src[0])
78 if w > WIDTH:
79 lines += 1
80 w = src[2] - src[0]
81 maxwidth = max(maxwidth, w)
82
83 xsize = maxwidth
84 ysize = lines * h
85
86 if xsize == 0 and ysize == 0:
87 return
88
89 self.ysize = h
90
91 # paste glyphs into bitmap
92 self.bitmap = Image.new("1", (xsize, ysize))
93 self.metrics: list[
94 tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]]
95 | None
96 ] = [None] * 256
97 x = y = 0
98 for i in range(256):
99 glyph = self[i]
100 if glyph:
101 d, dst, src, im = glyph
102 xx = src[2] - src[0]
103 x0, y0 = x, y
104 x = x + xx
105 if x > WIDTH:
106 x, y = 0, y + h
107 x0, y0 = x, y
108 x = xx
109 s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
110 self.bitmap.paste(im.crop(src), s)
111 self.metrics[i] = d, dst, s
112
113 def save(self, filename: str) -> None:
114 """Save font"""
115
116 self.compile()
117
118 # font data
119 if not self.bitmap:
120 msg = "No bitmap created"
121 raise ValueError(msg)
122 self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
123
124 # font metrics
125 with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
126 fp.write(b"PILfont\n")
127 fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!!
128 fp.write(b"DATA\n")
129 for id in range(256):
130 m = self.metrics[id]
131 if not m:
132 puti16(fp, (0,) * 10)
133 else:
134 puti16(fp, m[0] + m[1] + m[2])
135 