mirror of
https://github.com/freedoom/freedoom.git
synced 2025-09-01 13:25:46 -04:00
Python 2 is very near end-of-life, and Python3-compatible changes to a few scripts introduced compatibility problems with 2.7 again. It went unnoticed for me since my system symlinks "python" to "python3", but it broke the build on systems where that symlink is still python2. At this point in time, I feel it is worth targetting modern Python and forgetting about 2.7.
166 lines
4.9 KiB
Python
Executable file
166 lines
4.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
# Script to generate menu and intermission screen 'text' graphics for
|
|
# Freedoom, by compositing individual font character graphics together.
|
|
#
|
|
|
|
from PIL import Image
|
|
from glob import glob
|
|
import re
|
|
import sys
|
|
|
|
from config import *
|
|
from image_dimensions import *
|
|
from tint import image_tint
|
|
|
|
|
|
class TextGenerator(object):
|
|
def __init__(self, fontdir, kerning_table={}):
|
|
self.fontdir = fontdir
|
|
self.kerning_table = self.compile_kerning_table(kerning_table)
|
|
self.get_font_widths()
|
|
|
|
# Tinting parameters for colorizing text:
|
|
COLOR_BLUE = "#000001"
|
|
COLOR_RED = "#010000"
|
|
COLOR_WHITE = None
|
|
|
|
# Height of font in pixels.
|
|
FONT_HEIGHT = 15
|
|
FONT_LC_HEIGHT = 15 # 12
|
|
|
|
# If true, the font only has uppercase characters.
|
|
UPPERCASE_FONT = False
|
|
|
|
# Width of a space character in pixels.
|
|
SPACE_WIDTH = 7
|
|
LOWERCASE_RE = re.compile(r"^[a-z\!\. ]*$")
|
|
|
|
def compile_kerning_table(self, kerning_table):
|
|
"""Given a dictionary of kerning patterns, compile Regexps."""
|
|
|
|
result = {}
|
|
for pattern, adjust in kerning_table.items():
|
|
result[re.compile(pattern)] = adjust
|
|
return result
|
|
|
|
def get_font_widths(self):
|
|
charfiles = glob("%s/font*.png" % self.fontdir)
|
|
self.char_widths = {}
|
|
for c in range(128):
|
|
filename = self.char_filename(chr(c))
|
|
if filename not in charfiles:
|
|
continue
|
|
w, _ = get_image_dimensions(filename)
|
|
self.char_widths[chr(c)] = w
|
|
|
|
def __contains__(self, c):
|
|
return c in self.char_widths
|
|
|
|
def char_width(self, c):
|
|
return self.char_widths[c]
|
|
|
|
def char_filename(self, c):
|
|
return "%s/font%03d.png" % (self.fontdir, ord(c))
|
|
|
|
def kerning_adjust(self, char_1, char_2):
|
|
"""Get kerning adjustment for pair of characters.
|
|
|
|
Zero means no adjustment. A negative value adjusts to the
|
|
left and a positive value adjusts to the right.
|
|
"""
|
|
for pattern, adjust in self.kerning_table.items():
|
|
if pattern.match(char_1 + char_2):
|
|
return adjust
|
|
else:
|
|
return 0
|
|
|
|
def iterate_char_positions(self, text):
|
|
"""Iterate over characters in string, yielding character with
|
|
position it should be placed at in the output file.
|
|
"""
|
|
x = 0
|
|
last_c = " "
|
|
for c in text:
|
|
if c == " ":
|
|
x += self.SPACE_WIDTH
|
|
|
|
if c in self:
|
|
x += self.kerning_adjust(last_c, c)
|
|
|
|
yield c, x
|
|
|
|
# Characters overlap by one pixel.
|
|
x += self.char_width(c) - 1
|
|
|
|
last_c = c
|
|
|
|
# We need to add back the missing pixel from the right side
|
|
# of the last char.
|
|
x += 1
|
|
yield None, x
|
|
|
|
def text_width(self, text):
|
|
"""Given a string of text, get text width in pixels."""
|
|
for c, x in self.iterate_char_positions(text):
|
|
if c is None:
|
|
return x
|
|
|
|
def generate_graphic(self, text, color=None):
|
|
"""Get command to render text to a file
|
|
with the given background color.
|
|
"""
|
|
|
|
if self.UPPERCASE_FONT:
|
|
text = text.upper()
|
|
"""Command line construction helper, used in render functions"""
|
|
width = self.text_width(text)
|
|
|
|
if self.LOWERCASE_RE.match(text):
|
|
height = self.FONT_LC_HEIGHT
|
|
else:
|
|
height = self.FONT_HEIGHT
|
|
|
|
txt_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
|
for c, x in self.iterate_char_positions(text):
|
|
if c is None:
|
|
break
|
|
|
|
filename = self.char_filename(c)
|
|
char_image = Image.open(filename)
|
|
char_image.load()
|
|
int_image = Image.new("RGBA", txt_image.size, (0, 0, 0, 0))
|
|
int_image.paste(char_image, (x, height - self.FONT_HEIGHT))
|
|
txt_image = Image.alpha_composite(txt_image, int_image)
|
|
|
|
txt_image = image_tint(txt_image, color)
|
|
return txt_image
|
|
|
|
|
|
def generate_graphics(font, graphics, color=None):
|
|
for name, text in sorted(graphics.items()):
|
|
# write a makefile fragment
|
|
target = "%s.png" % name
|
|
image = font.generate_graphic(text, color=color)
|
|
image.save(target)
|
|
|
|
|
|
def generate_kerning_test(font):
|
|
pairs = []
|
|
for c1 in sorted(font.char_widths):
|
|
char1 = "%c" % c1
|
|
for c2 in sorted(font.char_widths):
|
|
char2 = "%c" % c2
|
|
if font.kerning_adjust(char1, char2) != 0:
|
|
pairs.append(char1 + char2)
|
|
|
|
cmd = font.generate_graphic(" ".join(pairs), "kerning.png")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
font = TextGenerator("fontchars", kerning_table=FONT_KERNING_RULES)
|
|
generate_graphics(font, red_graphics, color=font.COLOR_RED)
|
|
generate_graphics(font, blue_graphics, color=font.COLOR_BLUE)
|
|
generate_graphics(font, white_graphics, color=font.COLOR_WHITE)
|