Last active
November 11, 2023 16:14
-
-
Save petrblahos/1cbcce1eb0b4a5f54de5e6c4fbbb71e2 to your computer and use it in GitHub Desktop.
Draw glyphs on a pygame surface using fonttools and SVG.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import defaultdict | |
import io | |
import sys | |
import numpy as np | |
from scipy.special import comb | |
import pygame | |
from fontTools.pens.svgPathPen import (SVGPathPen, ) | |
from fontTools.pens.basePen import (BasePen, ) | |
from fontTools.ttLib import TTFont | |
class PGMFont: | |
WIDTH = 1280 | |
HEIGHT = 600 | |
def __init__(self, font): | |
self.font = font | |
self.units_per_em = self.font['head'].unitsPerEm | |
self.ascent = self.font["hhea"].ascent | |
self.descent = self.font["hhea"].descent | |
self.cmap = self.font['cmap'].getBestCmap() | |
self.glyph_set = self.font.getGlyphSet() | |
self.kerning_pairs = self.prepare_kerning(self.font) | |
def prepare_kerning(self, font: TTFont) -> dict: | |
pairs = defaultdict(int) | |
if not "kern" in self.font: | |
return pairs | |
for kern_table in self.font['kern'].kernTables: | |
assert kern_table.version == 0, "kern table version other than 0 not supported" | |
pairs.update(kern_table.kernTable) | |
return pairs | |
def prepare_window(self): | |
self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) | |
self.screen.fill((0, 0, 255)) | |
def create_svg_pair(self, text): | |
""" | |
Create 2 SVG files, one for stroke, one for fill. | |
""" | |
cmds_stroke = [] | |
cmds_fill = [] | |
pen = SVGPathPen(self.font.getGlyphSet()) | |
x0 = 0 | |
last_glyph = None | |
for unicode in text: | |
glyph_name = self.cmap.get(ord(unicode)) | |
if not glyph_name: | |
glyph_name = ".notdef" | |
glyph = self.glyph_set[glyph_name] | |
pen = SVGPathPen(self.glyph_set) | |
glyph.draw(pen) | |
commands = pen.getCommands() | |
x0 += self.kerning_pairs[(last_glyph, glyph_name)] | |
s = '<g transform="translate(%d %d) scale(1 -1)"><path d="%s" %%s /></g>\n' % ( | |
x0, | |
self.ascent, | |
commands, | |
) | |
x0 += glyph.width | |
cmds_stroke.append(s % 'stroke="#FFF" fill="none" stroke-width="50"') | |
cmds_fill.append(s % 'stroke="none" fill="#F00"') | |
last_glyph = glyph_name | |
svg_stroke = "\n".join([ | |
"""<?xml version="1.0" encoding="UTF-8"?>""", | |
"""<svg width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">""" % (x0, self.ascent - self.descent), | |
"\n".join(cmds_stroke) + "</svg>", | |
]) | |
svg_fill = "\n".join([ | |
"""<?xml version="1.0" encoding="UTF-8"?>""", | |
"""<svg width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">""" % (x0, self.ascent - self.descent), | |
"\n".join(cmds_fill) + "</svg>", | |
]) | |
return (svg_stroke, svg_fill) | |
def draw_text(self, surface, text): | |
(svg_stroke, svg_fill) = self.create_svg_pair(text) | |
img = pygame.image.load(io.BytesIO(svg_stroke.encode("utf-8"))) | |
surface.blit(img, (0, 0)) | |
img = pygame.image.load(io.BytesIO(svg_fill.encode("utf-8"))) | |
surface.blit(img, (0, 0)) | |
def draw_something(self): | |
surface = pygame.Surface((self.units_per_em * 10, self.units_per_em * 2)) | |
surface.fill((0, 0, 0)) | |
self.draw_text(surface, "VADoO0-") | |
scale = min((self.HEIGHT / surface.get_height(), self.WIDTH / surface.get_width())) | |
surface = pygame.transform.smoothscale( | |
surface, | |
(int(surface.get_width() * scale), int(surface.get_height() * scale)), | |
) | |
self.screen.blit(surface, (0, 0)) | |
def run(self): | |
clock = pygame.time.Clock() | |
do_run = True | |
while do_run: | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
do_run = False | |
break | |
self.draw_something() | |
pygame.display.flip() | |
if "__main__" == __name__: | |
f = TTFont("/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf") | |
pygame.init() | |
pgm = PGMFont(f) | |
pgm.prepare_window() | |
pgm.run() | |
pygame.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment