pil-font-maker 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pil-font-maker-0.6.0/LICENSE +21 -0
- pil-font-maker-0.6.0/PKG-INFO +47 -0
- pil-font-maker-0.6.0/README.md +37 -0
- pil-font-maker-0.6.0/pil_font_maker/__init__.py +4 -0
- pil-font-maker-0.6.0/pil_font_maker/pil_font_maker.py +616 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/PKG-INFO +47 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/SOURCES.txt +11 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/dependency_links.txt +1 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/entry_points.txt +5 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/requires.txt +1 -0
- pil-font-maker-0.6.0/pil_font_maker.egg-info/top_level.txt +1 -0
- pil-font-maker-0.6.0/setup.cfg +4 -0
- pil-font-maker-0.6.0/setup.py +26 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Marc Haarsma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pil-font-maker
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: A tool to make a PILfont from png images
|
|
5
|
+
Home-page: https://github.com/haarsmam/pil-font-maker
|
|
6
|
+
Author: Marc Haarsma
|
|
7
|
+
Author-email: marc.haarsma@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
|
|
11
|
+
# pil-font-maker
|
|
12
|
+
Extract a png image for each character in a PIL font and store them in a folder.
|
|
13
|
+
Contruct a PIL font from a folder containing a png image for each character.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
`python3 -m pip install pil-font-maker`
|
|
17
|
+
|
|
18
|
+
`python setup.py install`
|
|
19
|
+
|
|
20
|
+
# in folder where setup.py is located
|
|
21
|
+
|
|
22
|
+
`python -m pip install .`
|
|
23
|
+
|
|
24
|
+
## Supported Commands
|
|
25
|
+
|
|
26
|
+
On a command line type:
|
|
27
|
+
|
|
28
|
+
>pil-font-decode
|
|
29
|
+
|
|
30
|
+
Usage: pil-font-decode file.pil [folder]
|
|
31
|
+
|
|
32
|
+
Convert a pil font to a folder containing a png for each character present
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
>pil-font-encode
|
|
36
|
+
|
|
37
|
+
Usage: pil-font-encode folder [file.pil]
|
|
38
|
+
|
|
39
|
+
Convert a folder containing a png for each character to a pil font.
|
|
40
|
+
The pngs should be named char_0.png, char_1.png upto char_255.png
|
|
41
|
+
Omitted pngs will result in empty characters.
|
|
42
|
+
|
|
43
|
+
>pil-font-download
|
|
44
|
+
|
|
45
|
+
Usage: pil-font-download
|
|
46
|
+
|
|
47
|
+
Download some sample pil fonts from the PIL github repository
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# pil-font-maker
|
|
2
|
+
Extract a png image for each character in a PIL font and store them in a folder.
|
|
3
|
+
Contruct a PIL font from a folder containing a png image for each character.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
`python3 -m pip install pil-font-maker`
|
|
7
|
+
|
|
8
|
+
`python setup.py install`
|
|
9
|
+
|
|
10
|
+
# in folder where setup.py is located
|
|
11
|
+
|
|
12
|
+
`python -m pip install .`
|
|
13
|
+
|
|
14
|
+
## Supported Commands
|
|
15
|
+
|
|
16
|
+
On a command line type:
|
|
17
|
+
|
|
18
|
+
>pil-font-decode
|
|
19
|
+
|
|
20
|
+
Usage: pil-font-decode file.pil [folder]
|
|
21
|
+
|
|
22
|
+
Convert a pil font to a folder containing a png for each character present
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
>pil-font-encode
|
|
26
|
+
|
|
27
|
+
Usage: pil-font-encode folder [file.pil]
|
|
28
|
+
|
|
29
|
+
Convert a folder containing a png for each character to a pil font.
|
|
30
|
+
The pngs should be named char_0.png, char_1.png upto char_255.png
|
|
31
|
+
Omitted pngs will result in empty characters.
|
|
32
|
+
|
|
33
|
+
>pil-font-download
|
|
34
|
+
|
|
35
|
+
Usage: pil-font-download
|
|
36
|
+
|
|
37
|
+
Download some sample pil fonts from the PIL github repository
|
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
|
|
4
|
+
from genericpath import exists, isdir
|
|
5
|
+
from http.client import OK
|
|
6
|
+
from inspect import getframeinfo, stack
|
|
7
|
+
import requests
|
|
8
|
+
import struct
|
|
9
|
+
|
|
10
|
+
# raster , bitmap , font
|
|
11
|
+
|
|
12
|
+
# https://github.com/python-pillow/Pillow/pull/1072
|
|
13
|
+
|
|
14
|
+
# https://stackoverflow.com/questions/53021488/how-to-use-my-own-bitmap-font-in-pil-imagefont
|
|
15
|
+
|
|
16
|
+
# https://github.com/python-pillow/Pillow/blob/main/src/PIL/FontFile.py
|
|
17
|
+
# https://github.com/python-pillow/Pillow/blob/main/src/PIL/ImageFont.py
|
|
18
|
+
|
|
19
|
+
# https://stackoverflow.com/questions/30782756/a-better-way-to-add-text-to-ca-cvmat-than-cvputtext
|
|
20
|
+
|
|
21
|
+
# https://stackoverflow.com/questions/50268092/save-1-bit-deep-binary-image-in-python
|
|
22
|
+
# https://stackoverflow.com/questions/44997339/convert-python-image-to-single-channel-from-rgb-using-pil-or-scipy
|
|
23
|
+
|
|
24
|
+
# find . | grep .pil$
|
|
25
|
+
# ./Tests/fonts/ter-x20b-cp1250.pil
|
|
26
|
+
# ./Tests/fonts/ter-x20b-iso8859-1.pil
|
|
27
|
+
# ./Tests/fonts/ter-x20b-iso8859-2.pil
|
|
28
|
+
# ./Tests/fonts/10x20.pil
|
|
29
|
+
# ./Tests/images/courB08.pil
|
|
30
|
+
|
|
31
|
+
# find . | grep .pbm$
|
|
32
|
+
# ./Tests/fonts/ter-x20b-cp1250.pbm
|
|
33
|
+
# ./Tests/fonts/ter-x20b-iso8859-1.pbm
|
|
34
|
+
# ./Tests/fonts/ter-x20b-iso8859-2.pbm
|
|
35
|
+
# ./Tests/fonts/10x20.pbm
|
|
36
|
+
# ./Tests/images/hopper_1bit_plain.pbm
|
|
37
|
+
# ./Tests/images/courB08.pbm
|
|
38
|
+
# ./Tests/images/hopper_1bit.pbm
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# find . | grep hopper_1bit*.*
|
|
42
|
+
# ./Tests/images/hopper_1bit_plain.pbm
|
|
43
|
+
# ./Tests/images/hopper_1bit.pbm
|
|
44
|
+
|
|
45
|
+
import sys
|
|
46
|
+
import PIL # python -m pip install Pillow
|
|
47
|
+
from PIL import FontFile
|
|
48
|
+
from PIL import Image
|
|
49
|
+
import logging
|
|
50
|
+
import os
|
|
51
|
+
|
|
52
|
+
# import logging
|
|
53
|
+
# logging.basicConfig()
|
|
54
|
+
# logging.getLogger().setLevel(logging.DEBUG)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main():
|
|
58
|
+
|
|
59
|
+
argc = len(sys.argv)
|
|
60
|
+
if argc > 1:
|
|
61
|
+
|
|
62
|
+
command = sys.argv[1]
|
|
63
|
+
|
|
64
|
+
if command == "path":
|
|
65
|
+
return path()
|
|
66
|
+
|
|
67
|
+
if command == "download":
|
|
68
|
+
return download()
|
|
69
|
+
|
|
70
|
+
if command == "decode":
|
|
71
|
+
return decode(sys.argv[1:])
|
|
72
|
+
|
|
73
|
+
if command == "encode":
|
|
74
|
+
return encode(sys.argv[1:])
|
|
75
|
+
|
|
76
|
+
print("test")
|
|
77
|
+
test()
|
|
78
|
+
|
|
79
|
+
print("main")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def encode(args=None):
|
|
83
|
+
|
|
84
|
+
if args == None:
|
|
85
|
+
args = sys.argv
|
|
86
|
+
|
|
87
|
+
argc = len(args)
|
|
88
|
+
if argc == 1:
|
|
89
|
+
print("Usage: pil-font-encode folder [file.pil]")
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
folder = args[1]
|
|
93
|
+
|
|
94
|
+
if not os.path.exists(folder):
|
|
95
|
+
print("the provided folder does not exist : " + folder)
|
|
96
|
+
return 1
|
|
97
|
+
|
|
98
|
+
if not os.path.isdir(folder):
|
|
99
|
+
print("the provided folder is not a folder : " + folder)
|
|
100
|
+
return 1
|
|
101
|
+
|
|
102
|
+
file_name = os.path.basename(folder)
|
|
103
|
+
|
|
104
|
+
if argc == 3:
|
|
105
|
+
file_name = args[2]
|
|
106
|
+
|
|
107
|
+
if not file_name.endswith(".pil"):
|
|
108
|
+
file_name += ".pil"
|
|
109
|
+
|
|
110
|
+
empty_pil = FontFileMaker()
|
|
111
|
+
empty_pil.create_from_folder(folder)
|
|
112
|
+
empty_pil.save(file_name)
|
|
113
|
+
|
|
114
|
+
print(
|
|
115
|
+
"encoded",
|
|
116
|
+
empty_pil.char_count,
|
|
117
|
+
"characters from folder",
|
|
118
|
+
folder,
|
|
119
|
+
"to",
|
|
120
|
+
file_name,
|
|
121
|
+
"and",
|
|
122
|
+
os.path.splitext(file_name)[0] + ".pbm",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return 0
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def decode(args=None):
|
|
129
|
+
|
|
130
|
+
if args == None:
|
|
131
|
+
args = sys.argv
|
|
132
|
+
|
|
133
|
+
argc = len(args)
|
|
134
|
+
if argc == 1:
|
|
135
|
+
print("Usage: pil-font-decode file.pil [folder]")
|
|
136
|
+
return 1
|
|
137
|
+
|
|
138
|
+
file_name = args[1]
|
|
139
|
+
|
|
140
|
+
if not file_name.endswith(".pil"):
|
|
141
|
+
print("unexpected : file name does not end with '.pil'")
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
if not os.path.exists(file_name):
|
|
145
|
+
print("the provided file does not exist : " + file_name)
|
|
146
|
+
return 1
|
|
147
|
+
|
|
148
|
+
if not os.path.isfile(file_name):
|
|
149
|
+
print("the provided file is not a file : " + file_name)
|
|
150
|
+
return 1
|
|
151
|
+
|
|
152
|
+
folder = None
|
|
153
|
+
# folder too
|
|
154
|
+
if argc == 3:
|
|
155
|
+
|
|
156
|
+
folder = args[2]
|
|
157
|
+
|
|
158
|
+
if os.path.isfile(folder):
|
|
159
|
+
print("a file with this name exists : " + folder)
|
|
160
|
+
return 1
|
|
161
|
+
|
|
162
|
+
xxx = FontFileMaker(file_name)
|
|
163
|
+
xxx.save_glyps_as_png_with_offset(folder)
|
|
164
|
+
|
|
165
|
+
print("decoded", xxx.char_count, "characters to folder", xxx.root_path)
|
|
166
|
+
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def path():
|
|
171
|
+
exe = sys.executable
|
|
172
|
+
|
|
173
|
+
print(sys.executable)
|
|
174
|
+
print(os.path.dirname(exe))
|
|
175
|
+
|
|
176
|
+
scripts = os.path.join(os.path.dirname(exe), "Scripts")
|
|
177
|
+
print("set path=%path%;" + scripts)
|
|
178
|
+
print('setx path "%path%;' + scripts + '"')
|
|
179
|
+
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def download():
|
|
184
|
+
|
|
185
|
+
pil_urls = [
|
|
186
|
+
"https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/fonts/10x20.pil",
|
|
187
|
+
"https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/fonts/ter-x20b-cp1250.pil",
|
|
188
|
+
"https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/fonts/ter-x20b-iso8859-1.pil",
|
|
189
|
+
"https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/fonts/ter-x20b-iso8859-2.pil",
|
|
190
|
+
"https://raw.githubusercontent.com/python-pillow/Pillow/main/Tests/images/courB08.pil",
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
for pil_url in pil_urls:
|
|
194
|
+
|
|
195
|
+
download_url(pil_url)
|
|
196
|
+
|
|
197
|
+
# Replace .pil with .pbm in the URL to download the .pbm file
|
|
198
|
+
pbm_url = pil_url.replace(".pil", ".pbm")
|
|
199
|
+
|
|
200
|
+
download_url(pbm_url)
|
|
201
|
+
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def download_url(url):
|
|
206
|
+
response = requests.get(url)
|
|
207
|
+
|
|
208
|
+
# Check if the request was successful
|
|
209
|
+
if response.status_code == 200:
|
|
210
|
+
# Get the font name from the URL
|
|
211
|
+
file_name = url.split("/")[-1]
|
|
212
|
+
|
|
213
|
+
print(file_name)
|
|
214
|
+
|
|
215
|
+
# Write the content of the response to a file
|
|
216
|
+
with open(file_name, "wb") as font_file:
|
|
217
|
+
font_file.write(response.content)
|
|
218
|
+
else:
|
|
219
|
+
print("wrong url", url)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test():
|
|
224
|
+
|
|
225
|
+
logging.debug(PIL.__version__)
|
|
226
|
+
|
|
227
|
+
print("pil version", PIL.__version__)
|
|
228
|
+
print("package path", PIL.__file__)
|
|
229
|
+
print(os.path.dirname(PIL.__file__))
|
|
230
|
+
|
|
231
|
+
pil_10x20 = FontFileMaker("fonts/10x20.pil")
|
|
232
|
+
pil_10x20.save_glyps_as_png("test_10x20")
|
|
233
|
+
pil_10x20.save_glyps_as_png_with_offset("test_10x20_offset")
|
|
234
|
+
empty_pil = FontFileMaker("does not exist")
|
|
235
|
+
empty_pil.create_from_folder("test_10x20_offset")
|
|
236
|
+
empty_pil.save("test_10x20_offset")
|
|
237
|
+
|
|
238
|
+
marc_pil = FontFileMaker("fonts/example.pil")
|
|
239
|
+
marc_pil.save_glyps_as_png("test_example")
|
|
240
|
+
dst = "test_example_with_offset"
|
|
241
|
+
marc_pil.save_glyps_as_png_with_offset(dst)
|
|
242
|
+
marc_pil.save(dst) # glyps still have offset info.
|
|
243
|
+
# make_proporational(dst)
|
|
244
|
+
|
|
245
|
+
empty_pil = FontFileMaker("does not exist")
|
|
246
|
+
empty_pil.create_from_folder(dst)
|
|
247
|
+
empty_pil.save("empty")
|
|
248
|
+
# print(empty_pil.max_dims())
|
|
249
|
+
|
|
250
|
+
full_pil = FontFileMaker("empty.pil")
|
|
251
|
+
full_pil.save_glyps_as_png_with_offset("test_full")
|
|
252
|
+
|
|
253
|
+
# marc_pil.save_glyps_as_png()
|
|
254
|
+
# marc_new_pil = PILfont('marc_new.pil')
|
|
255
|
+
# courB08_pil = PILfont('courB08.pil')
|
|
256
|
+
# need to download pil fronts from
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class FontFileMaker(FontFile.FontFile):
|
|
260
|
+
|
|
261
|
+
def __del__(self):
|
|
262
|
+
|
|
263
|
+
logging.debug("destructor")
|
|
264
|
+
|
|
265
|
+
for glyph in self.glyph:
|
|
266
|
+
|
|
267
|
+
if glyph:
|
|
268
|
+
|
|
269
|
+
glyph[3].close()
|
|
270
|
+
|
|
271
|
+
def __init__(self, filename: str = ""):
|
|
272
|
+
|
|
273
|
+
super().__init__()
|
|
274
|
+
|
|
275
|
+
# so we init info and glyph
|
|
276
|
+
# self.info = {}
|
|
277
|
+
# self.glyph = [None] * 256
|
|
278
|
+
|
|
279
|
+
# metric
|
|
280
|
+
# bitmap
|
|
281
|
+
# ysize
|
|
282
|
+
|
|
283
|
+
self.char_count = 0
|
|
284
|
+
|
|
285
|
+
if not os.path.exists(filename): # empty font
|
|
286
|
+
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
if os.path.isdir(filename):
|
|
290
|
+
|
|
291
|
+
self.create_from_folder(filename)
|
|
292
|
+
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
if not filename.endswith(".pil"):
|
|
296
|
+
|
|
297
|
+
print("unexpected : file name does not end with '.pil'")
|
|
298
|
+
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
self.root_path = os.path.splitext(filename)[0]
|
|
302
|
+
|
|
303
|
+
self.filename = filename
|
|
304
|
+
|
|
305
|
+
self.bytes_per_glyph = 20
|
|
306
|
+
self.glyph_size = self.bytes_per_glyph * 256
|
|
307
|
+
self.header_size = os.path.getsize(filename) - self.glyph_size
|
|
308
|
+
|
|
309
|
+
# from super
|
|
310
|
+
# self.info = {}
|
|
311
|
+
# self.glyph = [None] * 256
|
|
312
|
+
|
|
313
|
+
self.read_glyphs() # needed, reads in glyph data
|
|
314
|
+
|
|
315
|
+
h = self.determine_font_height()
|
|
316
|
+
|
|
317
|
+
if self.font_height != h:
|
|
318
|
+
|
|
319
|
+
logging.debug("updating font_height", self.font_height, "->", h)
|
|
320
|
+
|
|
321
|
+
self.font_height = h
|
|
322
|
+
|
|
323
|
+
# constructor ??? / factory method / static method
|
|
324
|
+
def create_from_folder(self, folder: str):
|
|
325
|
+
|
|
326
|
+
# do not need to allocated, save -> compile does this.
|
|
327
|
+
# just read glyphs
|
|
328
|
+
|
|
329
|
+
if not os.path.isdir(folder):
|
|
330
|
+
print(folder + " is not a folder")
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
for i in range(256):
|
|
334
|
+
|
|
335
|
+
file_path = os.path.join(folder, "char_" + str(i) + ".png")
|
|
336
|
+
|
|
337
|
+
if os.path.isfile(file_path):
|
|
338
|
+
|
|
339
|
+
self.char_count += 1
|
|
340
|
+
|
|
341
|
+
im = Image.open(file_path)
|
|
342
|
+
|
|
343
|
+
w, h = im.size
|
|
344
|
+
|
|
345
|
+
# (( 6, 0), (1, -6, 4, -3), (3, 0, 6, 3)
|
|
346
|
+
# d dst to screen src from im
|
|
347
|
+
# ((10, 0), (0, -16, 10, 4), ( 0, 0, 10, 20)
|
|
348
|
+
# ((10, 0), (0, -16, 10, 4), (10, 0, 20, 20)
|
|
349
|
+
# ((10, 0), (0, -16, 10, 4), (20, 0, 30, 20)
|
|
350
|
+
# metrics = [max_w,0, (0, , w , y1) , ( )
|
|
351
|
+
|
|
352
|
+
y = int(h / 5) # 20 -> 4 11 -> 2
|
|
353
|
+
|
|
354
|
+
d = (w, 0)
|
|
355
|
+
dst = (0, y - h, w, y)
|
|
356
|
+
src = (0, 0, w, h) # individual bitmap not yet mapped to single bitmap
|
|
357
|
+
|
|
358
|
+
else:
|
|
359
|
+
|
|
360
|
+
im = Image.new("1", (0, 0))
|
|
361
|
+
|
|
362
|
+
w, h = im.size
|
|
363
|
+
|
|
364
|
+
d = (w, 0)
|
|
365
|
+
dst = (0, 0, w, 0)
|
|
366
|
+
src = (0, 0, w, h)
|
|
367
|
+
|
|
368
|
+
self.glyph[i] = d, dst, src, im
|
|
369
|
+
|
|
370
|
+
return self
|
|
371
|
+
|
|
372
|
+
def get_glyphs_with_height(self, height):
|
|
373
|
+
|
|
374
|
+
glyphs_with_height = []
|
|
375
|
+
|
|
376
|
+
for i, g in enumerate(self.glyph):
|
|
377
|
+
|
|
378
|
+
src = g[2]
|
|
379
|
+
|
|
380
|
+
h = src[3] - src[1]
|
|
381
|
+
|
|
382
|
+
if h == height:
|
|
383
|
+
|
|
384
|
+
glyphs_with_height.append(i)
|
|
385
|
+
|
|
386
|
+
return sorted(glyphs_with_height)
|
|
387
|
+
|
|
388
|
+
def determine_font_height(self):
|
|
389
|
+
|
|
390
|
+
md = self.max_dims()
|
|
391
|
+
|
|
392
|
+
h = md[3] - md[1]
|
|
393
|
+
|
|
394
|
+
return h
|
|
395
|
+
|
|
396
|
+
def save_glyps_as_png_with_offset(self, folder):
|
|
397
|
+
|
|
398
|
+
if folder == None:
|
|
399
|
+
folder = self.root_path
|
|
400
|
+
else:
|
|
401
|
+
self.root_path = folder
|
|
402
|
+
|
|
403
|
+
os.makedirs(folder, exist_ok=True)
|
|
404
|
+
|
|
405
|
+
for i, g in enumerate(self.glyph):
|
|
406
|
+
|
|
407
|
+
dst = g[1]
|
|
408
|
+
src = g[2]
|
|
409
|
+
|
|
410
|
+
w = src[2] - src[0]
|
|
411
|
+
h = src[3] - src[1]
|
|
412
|
+
|
|
413
|
+
h = self.font_height
|
|
414
|
+
|
|
415
|
+
# g[0] dst = g[1] src = g[2]
|
|
416
|
+
# 45 - (6, 0) (0, -4, 5, -3) ( 48, 0, 53, 1) 5 wide 10 - 1 = 9
|
|
417
|
+
# 46 . (6, 0) (1, -1, 3, 0) ( 53, 0, 55, 1) 2 wide 10 - 1 = 9
|
|
418
|
+
# 106 j (6, 0) (0, -7, 4, 2) (371, 0, 375, 9) 4 wide offset = 10 - 9 = 1
|
|
419
|
+
# 229 å (6, 0) (0, -9, 6, 0) ( 57, 10, 63, 19)
|
|
420
|
+
|
|
421
|
+
im_dst = Image.new("1", (w, h))
|
|
422
|
+
|
|
423
|
+
if (w > 0) and (h > 0):
|
|
424
|
+
|
|
425
|
+
self.char_count += 1
|
|
426
|
+
|
|
427
|
+
full_image = g[3]
|
|
428
|
+
|
|
429
|
+
cropped = full_image.crop(src)
|
|
430
|
+
|
|
431
|
+
offset = self.font_height - (dst[3] - dst[1])
|
|
432
|
+
offset = max(9 + dst[1], 0)
|
|
433
|
+
# print( i , 9+dst[1])
|
|
434
|
+
|
|
435
|
+
im_dst.paste(cropped, (0, offset))
|
|
436
|
+
|
|
437
|
+
file_path = os.path.join(folder, "char_" + str(i) + ".png")
|
|
438
|
+
|
|
439
|
+
im_dst.save(file_path)
|
|
440
|
+
|
|
441
|
+
def save_glyps_as_png(self, folder=None):
|
|
442
|
+
|
|
443
|
+
if folder == None:
|
|
444
|
+
|
|
445
|
+
folder = self.root_path
|
|
446
|
+
|
|
447
|
+
os.makedirs(folder, exist_ok=True)
|
|
448
|
+
|
|
449
|
+
for i, g in enumerate(self.glyph): # index and glyph_list
|
|
450
|
+
|
|
451
|
+
src = g[2]
|
|
452
|
+
|
|
453
|
+
w = src[2] - src[0]
|
|
454
|
+
h = src[3] - src[1]
|
|
455
|
+
|
|
456
|
+
if (w > 0) and (h > 0):
|
|
457
|
+
|
|
458
|
+
full_image = g[3]
|
|
459
|
+
|
|
460
|
+
cropped = full_image.crop(src)
|
|
461
|
+
|
|
462
|
+
file_path = os.path.join(folder, "char_" + str(i) + ".png")
|
|
463
|
+
|
|
464
|
+
cropped.save(file_path)
|
|
465
|
+
|
|
466
|
+
def save_font_data_as_png(self, filename=None):
|
|
467
|
+
|
|
468
|
+
if filename is None:
|
|
469
|
+
|
|
470
|
+
filename = self.filename
|
|
471
|
+
|
|
472
|
+
self.bitmap.save(
|
|
473
|
+
os.path.splitext(filename)[0] + ".png"
|
|
474
|
+
) # will also compile so just glyphs need to be okay
|
|
475
|
+
|
|
476
|
+
def max_dims(self):
|
|
477
|
+
|
|
478
|
+
w_min = 0
|
|
479
|
+
w_max = 0
|
|
480
|
+
h_min = 0
|
|
481
|
+
h_max = 0
|
|
482
|
+
|
|
483
|
+
for glyph in self.glyph:
|
|
484
|
+
if glyph:
|
|
485
|
+
|
|
486
|
+
dst = glyph[1]
|
|
487
|
+
|
|
488
|
+
if dst[0] < w_min:
|
|
489
|
+
|
|
490
|
+
w_min = dst[0]
|
|
491
|
+
|
|
492
|
+
if dst[2] > w_max:
|
|
493
|
+
# print(i,chr(i))
|
|
494
|
+
w_max = dst[2]
|
|
495
|
+
|
|
496
|
+
if dst[1] < h_min:
|
|
497
|
+
|
|
498
|
+
h_min = dst[1]
|
|
499
|
+
|
|
500
|
+
if dst[3] > h_max:
|
|
501
|
+
# print(i,chr(i))
|
|
502
|
+
h_max = dst[3]
|
|
503
|
+
|
|
504
|
+
# g[0] dst = g[1] src = g[2]
|
|
505
|
+
# 45 - (6, 0) (0, -4, 5, -3) ( 48, 0, 53, 1) w = 5 h = 1 10 - 1 = 9 h
|
|
506
|
+
# 46 . (6, 0) (1, -1, 3, 0) ( 53, 0, 55, 1) w = 2 h = 1 10 - 1 = 9
|
|
507
|
+
# 106 j (6, 0) (0, -7, 4, 2) (371, 0, 375, 9) w = 4 h = 9 offset = 10 - 9 = 1
|
|
508
|
+
# 229 å (6, 0) (0, -9, 6, 0) ( 57, 10, 63, 19) w = 6 h = 9 wide
|
|
509
|
+
|
|
510
|
+
return (w_min, h_min, w_max, h_max)
|
|
511
|
+
|
|
512
|
+
# w -1 0 1 2 3 4 5 6
|
|
513
|
+
# h -7 . . . . . . . .
|
|
514
|
+
# h -6 . . . . . . . .
|
|
515
|
+
# h -5
|
|
516
|
+
# h -4
|
|
517
|
+
# h -3
|
|
518
|
+
# h -2
|
|
519
|
+
# h -1
|
|
520
|
+
# h 0
|
|
521
|
+
# h 1
|
|
522
|
+
# h 2
|
|
523
|
+
|
|
524
|
+
def read_glyphs(self):
|
|
525
|
+
|
|
526
|
+
if os.path.isfile(self.root_path + ".png"):
|
|
527
|
+
|
|
528
|
+
im = Image.open(self.root_path + ".png")
|
|
529
|
+
|
|
530
|
+
if os.path.isfile(self.root_path + ".pbm"):
|
|
531
|
+
|
|
532
|
+
im = Image.open(self.root_path + ".pbm")
|
|
533
|
+
|
|
534
|
+
with open(self.root_path + ".pil", "rb") as file:
|
|
535
|
+
|
|
536
|
+
if file.readline() != b"PILfont\n":
|
|
537
|
+
raise SyntaxError("Not a PILfont file")
|
|
538
|
+
xxx = file.readline().split(b";")
|
|
539
|
+
|
|
540
|
+
self.font_height = int(xxx[6].decode("utf-8"))
|
|
541
|
+
confirm(len(xxx), 8)
|
|
542
|
+
confirm(xxx[7], b"\n")
|
|
543
|
+
# print('font_height',self.font_height)
|
|
544
|
+
|
|
545
|
+
self.info = [] # FIXME: should be a dictionary
|
|
546
|
+
while True:
|
|
547
|
+
s = file.readline()
|
|
548
|
+
if not s or s == b"DATA\n":
|
|
549
|
+
break
|
|
550
|
+
self.info.append(s)
|
|
551
|
+
|
|
552
|
+
# read PILfont metrics
|
|
553
|
+
data = file.read(256 * 20)
|
|
554
|
+
|
|
555
|
+
for i in range(256):
|
|
556
|
+
|
|
557
|
+
raw = struct.unpack(">hhhhhhhhhh", data[i * 20 : (i + 1) * 20])
|
|
558
|
+
|
|
559
|
+
d = (raw[0], raw[1])
|
|
560
|
+
dst = (raw[2], raw[3], raw[4], raw[5])
|
|
561
|
+
src = (raw[6], raw[7], raw[8], raw[9])
|
|
562
|
+
|
|
563
|
+
self.glyph[i] = (d, dst, src, im)
|
|
564
|
+
|
|
565
|
+
# c = get_char(i)
|
|
566
|
+
|
|
567
|
+
# print(i,c,d,dst,src)
|
|
568
|
+
|
|
569
|
+
# im.close() save needs to have an open image
|
|
570
|
+
|
|
571
|
+
def compile(self):
|
|
572
|
+
|
|
573
|
+
super().compile()
|
|
574
|
+
|
|
575
|
+
for i in range(256):
|
|
576
|
+
glyph = self[i]
|
|
577
|
+
if glyph:
|
|
578
|
+
d, dst, src, im = glyph
|
|
579
|
+
xx = src[2] - src[0]
|
|
580
|
+
if xx == 0:
|
|
581
|
+
self.metrics[i] = d, dst, (0, 0, 0, 0)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
#####################################################################
|
|
585
|
+
def get_char(i):
|
|
586
|
+
#################################################################
|
|
587
|
+
|
|
588
|
+
if i in [132, 133, 155]:
|
|
589
|
+
|
|
590
|
+
return ""
|
|
591
|
+
|
|
592
|
+
if i in [0, 7, 8, 9, 10, 12, 13, 132, 133]:
|
|
593
|
+
|
|
594
|
+
return "."
|
|
595
|
+
|
|
596
|
+
return chr(i)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
#####################################################################
|
|
600
|
+
def confirm(act, exp):
|
|
601
|
+
#################################################################
|
|
602
|
+
|
|
603
|
+
if act != exp:
|
|
604
|
+
|
|
605
|
+
caller = getframeinfo(stack()[1][0])
|
|
606
|
+
|
|
607
|
+
print("%s(%d)" % (caller.filename, caller.lineno))
|
|
608
|
+
print("expected : ", exp)
|
|
609
|
+
print(" actual : ", act)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
#####################################################################
|
|
613
|
+
if __name__ == "__main__":
|
|
614
|
+
#################################################################
|
|
615
|
+
|
|
616
|
+
sys.exit(main())
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pil-font-maker
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: A tool to make a PILfont from png images
|
|
5
|
+
Home-page: https://github.com/haarsmam/pil-font-maker
|
|
6
|
+
Author: Marc Haarsma
|
|
7
|
+
Author-email: marc.haarsma@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
|
|
11
|
+
# pil-font-maker
|
|
12
|
+
Extract a png image for each character in a PIL font and store them in a folder.
|
|
13
|
+
Contruct a PIL font from a folder containing a png image for each character.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
`python3 -m pip install pil-font-maker`
|
|
17
|
+
|
|
18
|
+
`python setup.py install`
|
|
19
|
+
|
|
20
|
+
# in folder where setup.py is located
|
|
21
|
+
|
|
22
|
+
`python -m pip install .`
|
|
23
|
+
|
|
24
|
+
## Supported Commands
|
|
25
|
+
|
|
26
|
+
On a command line type:
|
|
27
|
+
|
|
28
|
+
>pil-font-decode
|
|
29
|
+
|
|
30
|
+
Usage: pil-font-decode file.pil [folder]
|
|
31
|
+
|
|
32
|
+
Convert a pil font to a folder containing a png for each character present
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
>pil-font-encode
|
|
36
|
+
|
|
37
|
+
Usage: pil-font-encode folder [file.pil]
|
|
38
|
+
|
|
39
|
+
Convert a folder containing a png for each character to a pil font.
|
|
40
|
+
The pngs should be named char_0.png, char_1.png upto char_255.png
|
|
41
|
+
Omitted pngs will result in empty characters.
|
|
42
|
+
|
|
43
|
+
>pil-font-download
|
|
44
|
+
|
|
45
|
+
Usage: pil-font-download
|
|
46
|
+
|
|
47
|
+
Download some sample pil fonts from the PIL github repository
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
pil_font_maker/__init__.py
|
|
5
|
+
pil_font_maker/pil_font_maker.py
|
|
6
|
+
pil_font_maker.egg-info/PKG-INFO
|
|
7
|
+
pil_font_maker.egg-info/SOURCES.txt
|
|
8
|
+
pil_font_maker.egg-info/dependency_links.txt
|
|
9
|
+
pil_font_maker.egg-info/entry_points.txt
|
|
10
|
+
pil_font_maker.egg-info/requires.txt
|
|
11
|
+
pil_font_maker.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Pillow
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pil_font_maker
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
|
|
4
|
+
from setuptools import setup
|
|
5
|
+
from pil_font_maker import __version__
|
|
6
|
+
|
|
7
|
+
setup(
|
|
8
|
+
name="pil-font-maker",
|
|
9
|
+
version=__version__,
|
|
10
|
+
description="A tool to make a PILfont from png images",
|
|
11
|
+
long_description=open("README.md").read(),
|
|
12
|
+
long_description_content_type="text/markdown",
|
|
13
|
+
author="Marc Haarsma",
|
|
14
|
+
author_email="marc.haarsma@gmail.com",
|
|
15
|
+
url="https://github.com/haarsmam/pil-font-maker",
|
|
16
|
+
packages=["pil_font_maker"],
|
|
17
|
+
install_requires=["Pillow"],
|
|
18
|
+
entry_points={
|
|
19
|
+
"console_scripts": [
|
|
20
|
+
"pil-font-maker = pil_font_maker.pil_font_maker:main",
|
|
21
|
+
"pil-font-decode = pil_font_maker.pil_font_maker:decode",
|
|
22
|
+
"pil-font-encode = pil_font_maker.pil_font_maker:encode",
|
|
23
|
+
"pil-font-download = pil_font_maker.pil_font_maker:download",
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
)
|