Skip to content

Instantly share code, notes, and snippets.

@alterakey
Created September 12, 2011 14:48
Show Gist options
  • Save alterakey/1211448 to your computer and use it in GitHub Desktop.
Save alterakey/1211448 to your computer and use it in GitHub Desktop.
Rendering characters one-by-one with FT2+PIL
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# mass-render-chars.py: POC of massive character rendering
#
# Copyright (C) 2011 Takahiro Yoshimura <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
import freetype
import Image, ImageDraw
class FT2Bitmap(object):
def __init__(self, bitmap):
self.bitmap = bitmap
def to_pil_image(self):
data = ''.join([struct.pack('B', c) for c in self.bitmap.buffer])
return Image.frombuffer("L", (self.bitmap.width, self.bitmap.rows), data, "raw", "L", 0, 1)
class GlyphWriter(object):
def __init__(self, char, face, size, color):
self.char = char
self.face_name = face
self.char_size = size
self.color = color
def write(self):
mask = self._write_glyph()
im = Image.new('RGBA', mask.size, (0,0,0,255))
draw = ImageDraw.Draw(im)
draw.bitmap((0, 0), mask, self.color)
return im
def _load_glyph(self):
face = freetype.Face(self.face_name)
face.set_char_size(self.char_size * 64)
face.load_char(self.char.char, freetype.FT_LOAD_DEFAULT | freetype.FT_LOAD_NO_BITMAP)
return face.glyph.get_glyph()
def _write_glyph(self):
glyph = self._load_glyph()
blyph = glyph.to_bitmap(freetype.FT_RENDER_MODE_NORMAL, freetype.Vector(0,0))
self.char.set_bitmap_offset((blyph.left, -blyph.top))
bitmap = blyph.bitmap
return FT2Bitmap(bitmap).to_pil_image()
class Character(object):
def __init__(self, char=None, x=None, y=None, width=None, height=None):
self.char = char
self.x = x
self.y = y
self.width = width
self.height = height
self._left = None
self._top = None
def set_bitmap_offset(self, offset):
self._left, self._top = offset
def get_bitmap_offset(self):
return (self._left, self._top)
class TextMuncher(object):
def __init__(self, glyph_size, text, at=None):
self.glyph_size = glyph_size
self.text = text
if not at:
at = (0, 0)
self.at = at
def munch(self):
pitch = list(self.at)
width = self.glyph_size
height = self.glyph_size
for c in self.text:
yield Character(c, pitch[0], pitch[1], width, height)
pitch[0] += width * 0.85
class NormalMapping(object):
def __init__(self, glyph_size):
self.glyph_size = glyph_size
def map(self, char, glyph):
x, y = char.get_bitmap_offset()
y += self.glyph_size
x -= self.glyph_size / 2
y -= self.glyph_size / 2
return (char.x + x, char.y + y)
if __name__ == '__main__':
face_name = u'./fonts/ヒラギノ角ゴ Std W6.otf'.encode('utf-8')
glyph_size = 190
color = (255,255,255,255)
im = Image.new('RGBA', (5120, 5120), (0,0,0,255))
draw = ImageDraw.Draw(im)
text = u'じゅげむ、ジュゲム。…ゴコウの擦り切れ☆'
mapping = NormalMapping(glyph_size)
for char in TextMuncher(glyph_size, text, (95, 95)).munch():
glyph = GlyphWriter(char, face_name, glyph_size, color)._write_glyph()
draw.bitmap(mapping.map(char, glyph), glyph)
im.show()
im.save('hoge.png', 'png')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment