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 )
|
FT_Done_Face( self._FT_Face )
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Face({})'.format(self.family_name)
|
return 'Face({}, {})'.format(self.family_name, self.style_name)
|
||||||
|
|
||||||
def attach_file( self, filename ):
|
def attach_file( self, filename ):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from .config import build_ansi_color_tables, to_color
|
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.arrays import ArrayDatatype
|
||||||
from OpenGL.GL import (
|
from OpenGL.GL import (
|
||||||
@ -51,8 +51,7 @@ class CharGrid:
|
|||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.default_bg = self.original_bg = opts.background
|
self.default_bg = self.original_bg = opts.background
|
||||||
self.default_fg = self.original_fg = opts.foreground
|
self.default_fg = self.original_fg = opts.foreground
|
||||||
self.base_font_family = load_font_family(opts.font_family)
|
self.base_font_family = set_font_family(opts.font_family)
|
||||||
self.font_size = int(opts.font_size * 64)
|
|
||||||
self.apply_clear_color()
|
self.apply_clear_color()
|
||||||
|
|
||||||
def on_resize(self, window, w, h):
|
def on_resize(self, window, w, h):
|
||||||
|
|||||||
@ -3,11 +3,17 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import unicodedata
|
||||||
import re
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from functools import lru_cache
|
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):
|
def escape_family_name(name):
|
||||||
@ -23,8 +29,8 @@ def get_font_information(q, bold=False, italic=False):
|
|||||||
q += ':bold=200'
|
q += ':bold=200'
|
||||||
if italic:
|
if italic:
|
||||||
q += ':slant=100'
|
q += ':slant=100'
|
||||||
raw = subprocess.check_output(['fc-match', q, '-f', '%{file}\x31%{hinting}\x31%{hintstyle}']).decode('utf-8')
|
raw = subprocess.check_output(['fc-match', q, '-f', '%{file}\x1e%{hinting}\x1e%{hintstyle}']).decode('utf-8')
|
||||||
parts = raw.split('\x31')
|
parts = raw.split('\x1e')
|
||||||
hintstyle, hinting = 1, 'True'
|
hintstyle, hinting = 1, 'True'
|
||||||
if len(parts) == 3:
|
if len(parts) == 3:
|
||||||
path, hinting, hintstyle = parts
|
path, hinting, hintstyle = parts
|
||||||
@ -48,6 +54,58 @@ def get_font_files(family):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
current_font_family = current_font_family_name = None
|
||||||
def load_font_family(r):
|
|
||||||
return get_font_files(r)
|
|
||||||
|
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))
|
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():
|
def get_dpi():
|
||||||
if not hasattr(get_dpi, 'ans'):
|
if not hasattr(get_dpi, 'ans'):
|
||||||
m = glfw.glfwGetPrimaryMonitor()
|
m = glfw.glfwGetPrimaryMonitor()
|
||||||
@ -115,8 +123,5 @@ def get_dpi():
|
|||||||
vmode = glfw.glfwGetVideoMode(m)
|
vmode = glfw.glfwGetVideoMode(m)
|
||||||
dpix = vmode.width / (width / 25.4)
|
dpix = vmode.width / (width / 25.4)
|
||||||
dpiy = vmode.height / (height / 25.4)
|
dpiy = vmode.height / (height / 25.4)
|
||||||
get_dpi.ans = {'physical': (dpix, dpiy)}
|
get_dpi.ans = {'physical': (dpix, dpiy), 'logical': get_logical_dpi()}
|
||||||
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)))
|
|
||||||
return get_dpi.ans
|
return get_dpi.ans
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user