Code to render character cells
This commit is contained in:
parent
697db21c64
commit
809a833dc5
@ -992,7 +992,7 @@ class Face( object ):
|
||||
FT_Done_Face( self._FT_Face )
|
||||
|
||||
def __repr__(self):
|
||||
return 'Face({})'.format(self.family_name)
|
||||
return 'Face({}, {})'.format(self.family_name, self.style_name)
|
||||
|
||||
def attach_file( self, filename ):
|
||||
'''
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
from threading import Lock
|
||||
|
||||
from .config import build_ansi_color_tables, to_color
|
||||
from .fonts import load_font_family
|
||||
from .fonts import set_font_family
|
||||
|
||||
from OpenGL.arrays import ArrayDatatype
|
||||
from OpenGL.GL import (
|
||||
@ -51,8 +51,7 @@ class CharGrid:
|
||||
self.opts = opts
|
||||
self.default_bg = self.original_bg = opts.background
|
||||
self.default_fg = self.original_fg = opts.foreground
|
||||
self.base_font_family = load_font_family(opts.font_family)
|
||||
self.font_size = int(opts.font_size * 64)
|
||||
self.base_font_family = set_font_family(opts.font_family)
|
||||
self.apply_clear_color()
|
||||
|
||||
def on_resize(self, window, w, h):
|
||||
|
||||
@ -3,11 +3,17 @@
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import subprocess
|
||||
import unicodedata
|
||||
import re
|
||||
from collections import namedtuple
|
||||
from functools import lru_cache
|
||||
|
||||
from freetype import Face
|
||||
from freetype import (
|
||||
Face, FT_LOAD_RENDER, FT_LOAD_TARGET_NORMAL, FT_LOAD_TARGET_LIGHT,
|
||||
FT_LOAD_NO_HINTING, FT_PIXEL_MODE_GRAY
|
||||
)
|
||||
|
||||
from .utils import get_logical_dpi
|
||||
|
||||
|
||||
def escape_family_name(name):
|
||||
@ -23,8 +29,8 @@ def get_font_information(q, bold=False, italic=False):
|
||||
q += ':bold=200'
|
||||
if italic:
|
||||
q += ':slant=100'
|
||||
raw = subprocess.check_output(['fc-match', q, '-f', '%{file}\x31%{hinting}\x31%{hintstyle}']).decode('utf-8')
|
||||
parts = raw.split('\x31')
|
||||
raw = subprocess.check_output(['fc-match', q, '-f', '%{file}\x1e%{hinting}\x1e%{hintstyle}']).decode('utf-8')
|
||||
parts = raw.split('\x1e')
|
||||
hintstyle, hinting = 1, 'True'
|
||||
if len(parts) == 3:
|
||||
path, hinting, hintstyle = parts
|
||||
@ -48,6 +54,58 @@ def get_font_files(family):
|
||||
return ans
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def load_font_family(r):
|
||||
return get_font_files(r)
|
||||
current_font_family = current_font_family_name = None
|
||||
|
||||
|
||||
def set_font_family(family):
|
||||
global current_font_family, current_font_family_name
|
||||
if current_font_family_name != family:
|
||||
current_font_family = get_font_files(family)
|
||||
current_font_family_name = family
|
||||
return current_font_family['regular'].face
|
||||
|
||||
CharData = namedtuple('CharData', 'left width ascender descender')
|
||||
|
||||
|
||||
def render_char(text, size_in_pts, bold=False, italic=False):
|
||||
# TODO: Handle non-normalizable combining chars. Probably need to use
|
||||
# harfbuzz for that
|
||||
text = unicodedata.normalize('NFC', text)[0]
|
||||
key = 'regular'
|
||||
if bold:
|
||||
key = 'bi' if italic else 'bold'
|
||||
elif italic:
|
||||
key = 'italic'
|
||||
font = current_font_family.get(key) or current_font_family['regular']
|
||||
dpi = get_logical_dpi()
|
||||
sz = int(64 * size_in_pts)
|
||||
face = font.face
|
||||
face.set_char_size(width=sz, height=sz, hres=dpi[0], vres=dpi[1])
|
||||
flags = FT_LOAD_RENDER
|
||||
if font.hinting:
|
||||
if font.hintstyle >= 3:
|
||||
flags |= FT_LOAD_TARGET_NORMAL
|
||||
elif 0 < font.hintstyle < 3:
|
||||
flags |= FT_LOAD_TARGET_LIGHT
|
||||
else:
|
||||
flags |= FT_LOAD_NO_HINTING
|
||||
face.load_char(text, flags)
|
||||
bitmap = face.glyph.bitmap
|
||||
if bitmap.pixel_mode != FT_PIXEL_MODE_GRAY:
|
||||
raise ValueError('FreeType rendered the glyph with an unsupported pixel mode: {}'.format(bitmap.pixel_mode))
|
||||
width = bitmap.width
|
||||
ascender = face.glyph.bitmap_top
|
||||
descender = bitmap.rows - ascender
|
||||
left = face.glyph.bitmap_left
|
||||
|
||||
return bitmap.buffer, CharData(left, width, ascender, descender)
|
||||
|
||||
|
||||
def test_rendering(text='M', sz=144, family='monospace'):
|
||||
set_font_family(family)
|
||||
buf, char_data = render_char(text, sz)
|
||||
print(char_data)
|
||||
from PIL import Image
|
||||
img = Image.new('L', (char_data.width, char_data.ascender + char_data.descender))
|
||||
img.putdata(buf)
|
||||
img.show()
|
||||
|
||||
@ -108,6 +108,14 @@ def sanitize_title(x):
|
||||
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19]', '', x))
|
||||
|
||||
|
||||
def get_logical_dpi():
|
||||
if not hasattr(get_logical_dpi, 'ans'):
|
||||
raw = subprocess.check_output(['xdpyinfo']).decode('utf-8')
|
||||
m = re.search(r'^\s*resolution:\s*(\d+)+x(\d+)', raw, flags=re.MULTILINE)
|
||||
get_logical_dpi.ans = int(m.group(1)), int(m.group(2))
|
||||
return get_logical_dpi.ans
|
||||
|
||||
|
||||
def get_dpi():
|
||||
if not hasattr(get_dpi, 'ans'):
|
||||
m = glfw.glfwGetPrimaryMonitor()
|
||||
@ -115,8 +123,5 @@ def get_dpi():
|
||||
vmode = glfw.glfwGetVideoMode(m)
|
||||
dpix = vmode.width / (width / 25.4)
|
||||
dpiy = vmode.height / (height / 25.4)
|
||||
get_dpi.ans = {'physical': (dpix, dpiy)}
|
||||
raw = subprocess.check_output(['xdpyinfo']).decode('utf-8')
|
||||
m = re.search(r'^\s*resolution:\s*(\d+)+x(\d+)', raw, flags=re.MULTILINE)
|
||||
get_dpi.ans['logical'] = (int(m.group(1)), int(m.group(2)))
|
||||
get_dpi.ans = {'physical': (dpix, dpiy), 'logical': get_logical_dpi()}
|
||||
return get_dpi.ans
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user