Source

py-sdl2 / sdl2 / ext / font.py

Marcus von Appen 49a2c9a 

Marcus von Appen 5c3f489 

Marcus von Appen 49a2c9a 
Marcus von Appen 5c3f489 
Marcus von Appen 49a2c9a 
Marcus von Appen 5c3f489 




Marcus von Appen 49a2c9a 
Marcus von Appen 5c3f489 

Marcus von Appen 49a2c9a 














































































































































Marcus von Appen 3ff323b 
Marcus von Appen 49a2c9a 

Marcus von Appen 5c3f489 







Marcus von Appen b7cbf89 

"Ste...@gmail.co… b6e7d84 

Marcus von Appen 5c3f489 










"Ste...@gmail.co… b6e7d84 

Marcus von Appen 5c3f489 













Steven Johnson 05ea110 
Marcus von Appen 5c3f489 




"Ste...@gmail.co… b6e7d84 
Marcus von Appen 5c3f489 































































"Ste...@gmail.co… b6e7d84 
Marcus von Appen 5c3f489 


"Ste...@gmail.co… b6e7d84 
Marcus von Appen 5c3f489 


"Ste...@gmail.co… b6e7d84 
Marcus von Appen 5c3f489 
"Ste...@gmail.co… b6e7d84 
Marcus von Appen 5c3f489 

"Ste...@gmail.co… b6e7d84 

Marcus von Appen 5c3f489 







Steven Johnson 05ea110 
Marcus von Appen 5c3f489 
Marcus von Appen b7cbf89 
Marcus von Appen 5c3f489 






"Ste...@gmail.co… b6e7d84 

Marcus von Appen 5c3f489 










Steven Johnson 05ea110 
Marcus von Appen 5c3f489 
Steven Johnson 05ea110 




Marcus von Appen 5c3f489 







"""Font and text rendering routines."""
import os
from .. import surface, rect, pixels
from .compat import *
from .sprite import SoftwareSprite
from .color import Color, convert_to_color

_HASSDLTTF = True
try:
    from .. import sdlttf
except ImportError:
    _HASSDLTTF = False


__all__ = ["BitmapFont", "FontManager"]


class BitmapFont(object):
    """A bitmap graphics to character mapping.

    The BitmapFont class uses an image surface to find and render font
    character glyphs for text. It requires a mapping table, which
    denotes the characters available on the image.

    The mapping table is a list of strings, where each string reflects a
    'line' of characters on the image. Each character within each line
    has the same size as specified by the size argument.

    A typical mapping table might look like

      [ '0123456789',
        'ABCDEFGHIJ',
        'KLMNOPQRST',
        'UVWXYZ    ',
        'abcdefghij',
        'klmnopqrst',
        'uvwxyz    ',
        ',;.:!?+-()' ]
    """

    DEFAULTMAP = ["0123456789",
                  "ABCDEFGHIJ",
                  "KLMNOPQRST",
                  "UVWXYZ    ",
                  "abcdefghij",
                  "klmnopqrst",
                  "uvwxyz    ",
                  ",;.:!?+-()"
                  ]

    def __init__(self, imgsurface, size, mapping=None):
        """Creates a new BitmapFont instance from the passed image.

        Each character is expected to be of the same size (a 2-value tuple
        denoting the width and height) and to be in order of the passed
        mapping.
        """
        if mapping is None:
            self.mapping = list(BitmapFont.DEFAULTMAP)
        else:
            self.mapping = mapping
        self.offsets = {}
        if isinstance(imgsurface, SoftwareSprite):
            self.surface = imgsurface.surface
        #elif isinstance(surface, sprite.Sprite):
        #    TODO
        elif isinstance(imgsurface, surface.SDL_Surface):
            self.surface = imgsurface
        self.size = size[0], size[1]
        self._calculate_offsets()

    def _calculate_offsets(self):
        """Calculates the internal character offsets for each line."""
        self.offsets = {}
        offsets = self.offsets
        x, y = 0, 0
        w, h = self.size
        for line in self.mapping:
            x = 0
            for c in line:
                offsets[c] = rect.SDL_Rect(x, y, w, h)
                x += w
            y += h

    def render(self, text, bpp=None):
        """Renders the passed text on a new Sprite and returns it."""
        x, y = 0, 0
        tw, th = 0, 0
        w, h = self.size
        # TODO
        lines = text.split(os.linesep)
        for line in lines:
            tw = max(tw, sum([w for c in line]))
            th += h

        if bpp is None:
            bpp = self.surface.format.BitsPerPixel
        imgsurface = SoftwareSprite(tw, th, bpp)
        target = imgsurface.surface
        blit_surface = surface.SDL_BlitSurface
        fontsf = self.surface
        offsets = self.offsets

        srcr = rect.SDL_Rect(0, 0, 0, 0)
        for line in lines:
            for c in line:
                srcr.x = x
                srcr.y = y
                blit_surface(target, srcr, fontsf, offsets[c])
                x += w
            y += h
        return imgsurface

    def render_on(self, imgsurface, text, offset=(0, 0)):
        """Renders a text on the passed sprite, starting at a specific
        offset.

        The top-left start position of the text will be the passed offset and
        4-value tuple with the changed area will be returned.
        """
        x, y = offset
        w, h = self.size

        target = None
        if isinstance(imgsurface, SoftwareSprite):
            target = imgsurface.surface
        #elif isinstance(surface, sprite.Sprite):
        #    TODO
        elif isinstance(imgsurface, surface.SDL_Surface):
            target = imgsurface
        else:
            raise TypeError("unsupported surface type")

        lines = text.split(os.linesep)
        blit_surface = surface.SDL_BlitSurface
        fontsf = self.surface
        offsets = self.offsets

        srcr = rect.SDL_Rect(0, 0, 0, 0)
        for line in lines:
            for c in line:
                srcr.x = x
                srcr.y = y
                blit_surface(target, srcr, fontsf, offsets[c])
                x += w
            y += h
        return (offset[0], offset[1], x + w, y + h)

    def contains(self, c):
        """Checks, whether a certain character exists in the font."""
        return c in self.offsets

    def can_render(self, text):
        """Checks, whether all characters in the passed text can be rendered.
        """
        lines = text.split(os.linesep)
        for line in lines:
            for c in line:
                if c not in self.offsets:
                    return False
        return True


class FontManager(object):
    """Manage fonts and rendering of text."""
    def __init__(self, font_path, alias=None, size=16,
                 color=Color(255, 255, 255), bg_color=Color(0, 0, 0)):
        """Initialize the FontManager

        One font path must be given to initialize the FontManager. The
        default_font will be set to this font. color and bg_color
        will give the FontManager a default color. size is the default
        font size in pixels.
        """
        if not _HASSDLTTF:
            raise UnsupportedError("FontManager requires sdlttf support")
        if sdlttf.TTF_WasInit() == 0 and sdlttf.TTF_Init() != 0:
            raise SDLError()
        self.fonts = {}  # fonts = {alias: {size:font_ptr}}
        self.aliases = {}  # aliases = {alias:font_path}
        self._textcolor = pixels.SDL_Color(0, 0, 0)
        self._bgcolor = pixels.SDL_Color(255, 255, 255)
        self.color = color
        self.bg_color = bg_color
        self.size = size
        self._default_font = self.add(font_path, alias)

    def __del__(self):
        """Close all opened fonts."""
        self.close()

    def close(self):
        """Close all opened fonts."""
        for alias, fonts in self.fonts.items():
            for size, font in fonts.items():
                if font:
                    sdlttf.TTF_CloseFont(font)
        self.fonts = {}
        self.aliases = {}

    def add(self, font_path, alias=None, size=None):
        """Add a font to the Font Manager.

        alias is by default the font name. But another name can be
        passed. Returns the font pointer stored in self.fonts.
        """
        size = size or self.size
        if alias is None:
            # If no alias given, take the font name as alias
            basename = os.path.basename(font_path)
            alias = os.path.splitext(basename)[0]
            if alias in self.fonts:
                if size in self.fonts[alias] and self.fonts[alias]:
                    # font with selected size already opened
                    return
                else:
                    self._change_font_size(alias, size)
                    return
            else:
                if not os.path.isfile(font_path):
                    raise IOError("Cannot find %s" % font_path)

        font = self._load_font(font_path, size)
        self.aliases[alias] = font_path
        self.fonts[alias] = {}
        self.fonts[alias][size] = font
        return font

    def _load_font(self, font_path, size):
        """Helper function to open the font.

        Raises an exception if something went wrong.
        """
        font = sdlttf.TTF_OpenFont(byteify(font_path, "utf-8"), size)
        if font is None:
            raise SDLError()
        return font

    def _change_font_size(self, alias, size):
        """Loads an already opened font in another size."""
        if alias not in self.fonts:
            raise KeyError("Font %s not loaded in FontManager" % alias)
        font = self._load_font(self.aliases[alias], size)
        self.fonts[alias][size] = font

    @property
    def color(self):
        """The text color to be used."""
        return Color(self._textcolor.r, self._textcolor.g, self._textcolor.b,
                     self._textcolor.a)

    @color.setter
    def color(self, value):
        """The text color to be used."""
        c = convert_to_color(value)
        self._textcolor = pixels.SDL_Color(c.r, c.g, c.b, c.a)

    @property
    def bg_color(self):
        """The background color to be used."""
        return Color(self._bgcolor.r, self._bgcolor.g, self._bgcolor.b,
                     self._bgcolor.a)

    @bg_color.setter
    def bg_color(self, value):
        """The background color to be used."""
        c = convert_to_color(value)
        self._bgcolor = pixels.SDL_Color(c.r, c.g, c.b, c.a)

    @property
    def default_font(self):
        """Returns the name of the current default_font."""
        for alias in self.fonts:
            for size, font in self.fonts[alias].items():
                if font == self._default_font:
                    return alias

    @default_font.setter
    def default_font(self, value):
        """value must be a font alias

        Set the default_font to the given font name alias,
        provided it's loaded in the font manager.
        """
        alias = value
        size = self.size
        if alias not in self.fonts:
            raise ValueError("Font %s not loaded in FontManager" % alias)
        # Check if size is already loaded, otherwise do it.
        if size not in self.fonts[alias]:
            self._change_font_size(alias, size)
            size = list(self.fonts[alias].keys())[0]
        self._default_font = self.fonts[alias][size]

    def render(self, text, alias=None, size=None, width=None, color=None,
               bg_color=None, **kwargs):
        """Renders text to a surface.

        This method uses the font designated by the alias or the
        default_font.  A size can be passed even if the font was not
        loaded with this size.  A width can be given for line wrapping.
        If no bg_color or color are given, it will default to the
        FontManager's bg_color and color.
        """
        alias = alias or self.default_font
        size = size or self.size
        if bg_color is None:
            bg_color = self._bgcolor
        elif not isinstance(bg_color, pixels.SDL_Color):
            c = convert_to_color(bg_color)
            bg_color = pixels.SDL_Color(c.r, c.g, c.b, c.a)
        if color is None:
            color = self._textcolor
        elif not isinstance(color, pixels.SDL_Color):
            c = convert_to_color(color)
            bg_color = pixels.SDL_Color(c.r, c.g, c.b, c.a)
        if len(self.fonts) == 0:
            raise TypeError("There are no fonts selected.")
        font = self._default_font
        if alias not in self.aliases:
            raise KeyError("Font %s not loaded" % font)
        elif size not in self.fonts[alias]:
            self._change_font_size(alias, size)
        font = self.fonts[alias][size]
        text = byteify(text, "utf-8")
        if width:
            surface = sdlttf.TTF_RenderUTF8_Blended_Wrapped(font, text,
                                                            color, width)
        elif bg_color == pixels.SDL_Color(0, 0, 0):
            surface = sdlttf.TTF_RenderUTF8_Blended(font, text, color)
        else:
            surface = sdlttf.TTF_RenderUTF8_Shaded(font, text, color, bg_color)
        return surface.contents