sync pyglet
This commit is contained in:
parent
c79d98f89e
commit
05c39fefd2
@ -19,12 +19,17 @@ by this package.
|
||||
import os
|
||||
import sys
|
||||
import weakref
|
||||
from typing import Dict, Union, BinaryIO, Optional, List, Iterable
|
||||
|
||||
import pyglet
|
||||
from pyglet.font.user import UserDefinedFont
|
||||
from pyglet import gl
|
||||
|
||||
|
||||
if not getattr(sys, 'is_pyglet_doc_run', False):
|
||||
def _get_system_font_class():
|
||||
"""Get the appropriate class for the system being used.
|
||||
Pyglet relies on OS dependent font systems for loading fonts and glyph creation.
|
||||
"""
|
||||
if pyglet.compat_platform == 'darwin':
|
||||
from pyglet.font.quartz import QuartzFont
|
||||
_font_class = QuartzFont
|
||||
@ -37,18 +42,86 @@ if not getattr(sys, 'is_pyglet_doc_run', False):
|
||||
else:
|
||||
from pyglet.font.win32 import GDIPlusFont
|
||||
_font_class = GDIPlusFont
|
||||
|
||||
else:
|
||||
from pyglet.font.freetype import FreeTypeFont
|
||||
_font_class = FreeTypeFont
|
||||
|
||||
return _font_class
|
||||
|
||||
def have_font(name):
|
||||
|
||||
def create_font(name: str, mappings: Dict[str, pyglet.image.ImageData], default_char: str,
|
||||
ascent: Optional[float] = None,
|
||||
descent: Optional[float] = None, size: Optional[float] = None, bold: bool = False,
|
||||
italic: bool = False, stretch: bool = False, dpi: Optional[float] = None,
|
||||
font_class=UserDefinedFont):
|
||||
"""
|
||||
Create a custom font with the mappings provided.
|
||||
|
||||
If a character in a string is not mapped in the font, it will use the default_char as fallback.
|
||||
|
||||
Default size is 12.
|
||||
Ascent and descent will use the image dimensions as default, but can be provided.
|
||||
|
||||
The rest of the font parameters are used for font lookups.
|
||||
"""
|
||||
# Arbitrary default size
|
||||
if size is None:
|
||||
size = 12
|
||||
|
||||
if dpi is None:
|
||||
dpi = 96
|
||||
|
||||
# Locate or create font cache
|
||||
shared_object_space = gl.current_context.object_space
|
||||
if not hasattr(shared_object_space, 'pyglet_font_font_cache'):
|
||||
shared_object_space.pyglet_font_font_cache = weakref.WeakValueDictionary()
|
||||
shared_object_space.pyglet_font_font_hold = []
|
||||
shared_object_space.pyglet_font_font_name_match = {} # Match a tuple to specific name to reduce lookups.
|
||||
|
||||
font_cache = shared_object_space.pyglet_font_font_cache
|
||||
font_hold = shared_object_space.pyglet_font_font_hold
|
||||
|
||||
# Look for font name in font cache
|
||||
descriptor = (name, size, bold, italic, stretch, dpi)
|
||||
if descriptor in font_cache:
|
||||
raise Exception("A font with these parameters has already been created.", descriptor)
|
||||
|
||||
if _system_font_class.have_font(name):
|
||||
raise Exception(f"Font name: '{name}' already exists within the system fonts.")
|
||||
|
||||
# Not in cache, create from scratch
|
||||
font = font_class(mappings, default_char, name, ascent, descent, size, bold, italic, stretch, dpi)
|
||||
|
||||
# Save parameters for new-style layout classes to recover
|
||||
# TODO: add properties to the Font classes, so these can be queried:
|
||||
font.size = size
|
||||
font.bold = bold
|
||||
font.italic = italic
|
||||
font.stretch = stretch
|
||||
font.dpi = dpi
|
||||
|
||||
if name not in _user_fonts:
|
||||
_user_fonts.append(name)
|
||||
|
||||
# Cache font in weak-ref dictionary to avoid reloading while still in use
|
||||
font_cache[descriptor] = font
|
||||
|
||||
# Hold onto refs of last three loaded fonts to prevent them being
|
||||
# collected if momentarily dropped.
|
||||
del font_hold[3:]
|
||||
font_hold.insert(0, font)
|
||||
|
||||
return font
|
||||
|
||||
|
||||
def have_font(name: str) -> bool:
|
||||
"""Check if specified system font name is available."""
|
||||
return _font_class.have_font(name)
|
||||
return name in _user_fonts or _system_font_class.have_font(name)
|
||||
|
||||
|
||||
def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None):
|
||||
def load(name: Optional[Union[str, Iterable[str]]] = None, size: Optional[float] = None, bold: bool = False,
|
||||
italic: bool = False,
|
||||
stretch: bool = False, dpi: Optional[float] = None):
|
||||
"""Load a font for rendering.
|
||||
|
||||
:Parameters:
|
||||
@ -101,7 +174,7 @@ def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None
|
||||
# Find first matching name, cache it.
|
||||
found_name = None
|
||||
for n in name:
|
||||
if _font_class.have_font(n):
|
||||
if n in _user_fonts or _system_font_class.have_font(n):
|
||||
found_name = n
|
||||
break
|
||||
|
||||
@ -114,7 +187,7 @@ def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None
|
||||
return font_cache[descriptor]
|
||||
|
||||
# Not in cache, create from scratch
|
||||
font = _font_class(name, size, bold=bold, italic=italic, stretch=stretch, dpi=dpi)
|
||||
font = _system_font_class(name, size, bold=bold, italic=italic, stretch=stretch, dpi=dpi)
|
||||
|
||||
# Save parameters for new-style layout classes to recover
|
||||
# TODO: add properties to the Font classes, so these can be queried:
|
||||
@ -135,7 +208,12 @@ def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None
|
||||
return font
|
||||
|
||||
|
||||
def add_file(font):
|
||||
if not getattr(sys, 'is_pyglet_doc_run', False):
|
||||
_system_font_class = _get_system_font_class()
|
||||
_user_fonts = []
|
||||
|
||||
|
||||
def add_file(font: Union[str, BinaryIO]):
|
||||
"""Add a font to pyglet's search path.
|
||||
|
||||
In order to load a font that is not installed on the system, you must
|
||||
@ -156,7 +234,7 @@ def add_file(font):
|
||||
font = open(font, 'rb')
|
||||
if hasattr(font, 'read'):
|
||||
font = font.read()
|
||||
_font_class.add_font_data(font)
|
||||
_system_font_class.add_font_data(font)
|
||||
|
||||
|
||||
def add_directory(directory):
|
||||
|
86
libs/pyglet/font/user.py
Normal file
86
libs/pyglet/font/user.py
Normal file
@ -0,0 +1,86 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
import pyglet
|
||||
from pyglet.font import base
|
||||
|
||||
|
||||
class UserGlyphRenderer(base.GlyphRenderer):
|
||||
def __init__(self, font: 'UserDefinedFont'):
|
||||
self._font = font
|
||||
self._font.glyphs[self._font.default_char] = self.render(self._font.default_char)
|
||||
super().__init__(font)
|
||||
|
||||
def render(self, text: str):
|
||||
image_data = self._font.mappings[text]
|
||||
glyph = self._font.create_glyph(image_data)
|
||||
glyph.set_bearings(-self._font.descent, 0, image_data.width)
|
||||
return glyph
|
||||
|
||||
|
||||
class UserDefinedFont(base.Font):
|
||||
"""A basic UserDefinedFont, it takes a mappings of ImageData """
|
||||
glyph_renderer_class = UserGlyphRenderer
|
||||
|
||||
def __init__(self, mappings: Dict[str, pyglet.image.ImageData], default_char: str, name: str, ascent: float,
|
||||
descent: float, size: float, bold: bool = False, italic: bool = False, stretch: bool = False,
|
||||
dpi: int = None,
|
||||
locale: Optional[str] = None):
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self.mappings: Dict[str, pyglet.image.ImageData] = mappings
|
||||
|
||||
if default_char not in self.mappings:
|
||||
raise Exception(f"Default character: '{default_char}' must exist within your mappings.")
|
||||
|
||||
if ascent is None or descent is None:
|
||||
image = list(mappings.values())[0]
|
||||
|
||||
if ascent is None:
|
||||
ascent = image.height
|
||||
|
||||
if descent is None:
|
||||
descent = 0
|
||||
|
||||
self.ascent = ascent
|
||||
self.descent = descent
|
||||
self.default_char = default_char
|
||||
|
||||
self.bold = bold
|
||||
self.italic = italic
|
||||
self.stretch = stretch
|
||||
self.dpi = dpi
|
||||
self.size = size
|
||||
self.locale = locale
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def get_glyphs(self, text):
|
||||
"""Create and return a list of Glyphs for `text`.
|
||||
|
||||
If any characters do not have a known glyph representation in this
|
||||
font, a substitution will be made.
|
||||
|
||||
:Parameters:
|
||||
`text` : str or unicode
|
||||
Text to render.
|
||||
|
||||
:rtype: list of `Glyph`
|
||||
"""
|
||||
glyph_renderer = None
|
||||
glyphs = [] # glyphs that are committed.
|
||||
for c in base.get_grapheme_clusters(str(text)):
|
||||
# Get the glyph for 'c'. Hide tabs (Windows and Linux render
|
||||
# boxes)
|
||||
if c == '\t':
|
||||
c = ' '
|
||||
if c not in self.glyphs:
|
||||
if not glyph_renderer:
|
||||
glyph_renderer = self.glyph_renderer_class(self)
|
||||
if c not in self.mappings:
|
||||
c = self.default_char
|
||||
else:
|
||||
self.glyphs[c] = glyph_renderer.render(c)
|
||||
glyphs.append(self.glyphs[c])
|
||||
return glyphs
|
Loading…
Reference in New Issue
Block a user