sync pyglet
This commit is contained in:
parent
c79d98f89e
commit
05c39fefd2
@ -19,12 +19,17 @@ by this package.
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import weakref
|
import weakref
|
||||||
|
from typing import Dict, Union, BinaryIO, Optional, List, Iterable
|
||||||
|
|
||||||
import pyglet
|
import pyglet
|
||||||
|
from pyglet.font.user import UserDefinedFont
|
||||||
from pyglet import gl
|
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':
|
if pyglet.compat_platform == 'darwin':
|
||||||
from pyglet.font.quartz import QuartzFont
|
from pyglet.font.quartz import QuartzFont
|
||||||
_font_class = QuartzFont
|
_font_class = QuartzFont
|
||||||
@ -37,18 +42,86 @@ if not getattr(sys, 'is_pyglet_doc_run', False):
|
|||||||
else:
|
else:
|
||||||
from pyglet.font.win32 import GDIPlusFont
|
from pyglet.font.win32 import GDIPlusFont
|
||||||
_font_class = GDIPlusFont
|
_font_class = GDIPlusFont
|
||||||
|
|
||||||
else:
|
else:
|
||||||
from pyglet.font.freetype import FreeTypeFont
|
from pyglet.font.freetype import FreeTypeFont
|
||||||
_font_class = 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."""
|
"""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.
|
"""Load a font for rendering.
|
||||||
|
|
||||||
:Parameters:
|
: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.
|
# Find first matching name, cache it.
|
||||||
found_name = None
|
found_name = None
|
||||||
for n in name:
|
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
|
found_name = n
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -114,7 +187,7 @@ def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None
|
|||||||
return font_cache[descriptor]
|
return font_cache[descriptor]
|
||||||
|
|
||||||
# Not in cache, create from scratch
|
# 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
|
# Save parameters for new-style layout classes to recover
|
||||||
# TODO: add properties to the Font classes, so these can be queried:
|
# 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
|
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.
|
"""Add a font to pyglet's search path.
|
||||||
|
|
||||||
In order to load a font that is not installed on the system, you must
|
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')
|
font = open(font, 'rb')
|
||||||
if hasattr(font, 'read'):
|
if hasattr(font, 'read'):
|
||||||
font = font.read()
|
font = font.read()
|
||||||
_font_class.add_font_data(font)
|
_system_font_class.add_font_data(font)
|
||||||
|
|
||||||
|
|
||||||
def add_directory(directory):
|
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