Commits

Anonymous committed a729ce5

pygame.font implemented

  • Participants
  • Parent commits 3c8eb8d
  • Branches ctypes-soc

Comments (0)

Files changed (6)

                 ('b', c_ubyte),
                 ('unused', c_ubyte)]
 
-    def __init__(self, r=0, g=0, b=0):
+    def __init__(self, r=0, g=0, b=0, unused=0):
         self.r = r
         self.g = g
         self.b = b
+#!/usr/bin/env python
+
+'''Pygame module for loading and rendering fonts.
+
+The font module allows for rendering TrueType fonts into a new Surface
+object. This module is optional and requires SDL.ttf as a dependency. You
+should test that pygame.font is available and initialized before attempting
+to use the module.
+ 
+Most of the work done with fonts are done by using the actual Font objects.
+The module by itself only has routines to initialize the module and create
+Font objects with pygame.font.Font().
+ 
+You can load fonts from the system by using the pygame.font.SysFont()
+function. There are a few other functions to help lookup the system fonts.
+
+Pygame comes with a builtin default font. This can always be accessed by
+passing None as the font name.
+'''
+
+__docformat__ = 'restructuredtext'
+__version__ = '$Id$'
+
+from SDL import *
+from SDL.ttf import *
+
+import pygame.base
+
+_font_initialized = 0
+_font_defaultname = 'freesansbold.ttf'
+
+def __PYGAMEinit__():
+    global _font_initialized
+    if not _font_initialized: 
+        pygame.base.register_quit(_font_autoquit)
+
+        try:
+            TTF_Init()
+            _font_initialized = 1
+        except:
+            pass
+    return _font_initialized
+
+def _font_autoquit():
+    global _font_initialized
+    if _font_initialized:
+        _font_initialized = 0
+        TTF_Quit()
+
+def init():
+    '''Initialize the font module.
+
+    This method is called automatically by pygame.init().  It initializes the
+    font module. The module must be initialized before any other functions
+    will work.
+
+    It is safe to call this function more than once.
+    '''
+    __PYGAMEinit__()
+
+def quit():
+    '''Uninitialize the font module.
+
+    Manually uninitialize SDL_ttf's font system. This is called automatically
+    by pygame.quit().
+
+    It is safe to call this function even if font is currently not
+    initialized.
+    '''
+    _font_autoquit()
+
+def get_init():
+    '''Determine if the font module is initialized.
+
+    :rtype: bool
+    '''
+    return _font_initialized
+
+def get_default_font():
+    '''Get the filename of the default font.
+
+    Return the filename of the system font. This is not the full path to the
+    file.  This file can usually be found in the same directory as the font
+    module, but it can also be bundled in separate archives.
+
+    :rtype: str
+    '''
+    return _font_defaultname
+
+class Font(object):
+    __slots__ = ['_font']
+
+    def __init__(self, file, size):
+        '''Create a new Font object from a file.
+
+        Load a new font from a given filename or a python file object. The
+        size is the height of the font in pixels. If the filename is None the
+        Pygame default font will be loaded. If a font cannot be loaded from
+        the arguments given an exception will be raised. Once the font is
+        created the size cannot be changed. 
+
+        Font objects are mainly used to render text into new Surface objects.
+        The render can emulate bold or italic features, but it is better to
+        load from a font with actual italic or bold glyphs. The rendered text
+        can be regular strings or unicode.
+
+        :Parameters:
+            `file` : str or file-like object
+                Optional filename of font.
+            `size` : int
+                Size of font, in points.
+
+        '''
+        if not _font_initialized:
+            raise pygame.base.error, 'font not initialized'
+
+        size = max(1, size)
+
+        if not file:
+            raise NotImplemented, 'TODO: default font'
+
+        if hasattr(file, 'read'):
+            rw = SDL_RWFromObject(file)
+            # XXX: Differ from pygame: don't freesrc here
+            font = TTF_OpenFontRW(rw, 0, size)
+        else:
+            font = TTF_OpenFont(file, size)
+        self._font = font
+
+    def __del__(self):
+        if self._font and _font_initialized:
+            TTF_CloseFont(self._font)
+
+    def render(self, text, antialias, color, background=None):
+        '''Draw text on a new Surface.
+
+        This creates a new Surface with the specified text rendered on it.
+        Pygame provides no way to directly draw text on an existing Surface:
+        instead you must use Font.render() to create an image (Surface) of the
+        text, then blit this image onto another Surface.
+
+        The text can only be a single line: newline characters are not
+        rendered.  The antialias argument is a boolean: if true the characters
+        will have smooth edges. The color argument is the color of the text
+        [e.g.: (0,0,255) for blue]. The optional background argument is a
+        color to use for the text background. If no background is passed the
+        area outside the text will be transparent.
+
+        The Surface returned will be of the dimensions required to hold the
+        text.  (the same as those returned by Font.size()).  If an empty
+        string is passed for the text, a blank surface will be returned that
+        is one pixel wide and the height of the font.
+
+        Depending on the type of background and antialiasing used, this
+        returns different types of Surfaces. For performance reasons, it is
+        good to know what type of image will be used. If antialiasing is not
+        used, the return image will always be an 8bit image with a two color
+        palette. If the background is transparent a colorkey will be set.
+        Antialiased images are rendered to 24-bit RGB images. If the
+        background is transparent a pixel alpha will be included.
+
+        Optimization: if you know that the final destination for the text (on
+        the screen) will always have a solid background, and the text is
+        antialiased, you can improve performance by specifying the background
+        color. This will cause the resulting image to maintain transparency
+        information by colorkey rather than (much less efficient) alpha
+        values.
+
+        If you render '\n' a unknown char will be rendered.  Usually a
+        rectangle.  Instead you need to handle new lines yourself.
+
+        Font rendering is not thread safe: only a single thread can render
+        text any time.
+        
+        :Parameters:
+            `text` : str or unicode
+                Text to render
+            `antialias` : bool
+                If True, apply antialiasing to glyphs.
+            `color` : (int, int, int)
+                RGB color of glyphs.
+            `background` : (int, int, int)
+                Optional RGB color of background.
+
+        :rtype: `Surface`
+        '''
+        font = self._font
+        foreground = SDL_Color(*pygame.base._rgba_from_obj(color))
+        if background:
+            background = SDL_Color(*pygame.base._rgba_from_obj(background))
+
+        if antialias:
+            if not background:
+                surf = TTF_RenderText_Blended(font, text, foreground)
+            else:
+                surf = TTF_RenderText_Shaded(font, text, foreground, background)
+        else:
+            surf = TTF_RenderText_Solid(font, text, foreground)
+
+        if not antialias and background:
+            # Add color key
+            SDL_SetColorKey(surf, 0, 0)
+            surf.format.palette.colors[0].r = background.r
+            surf.format.palette.colors[0].g = background.g
+            surf.format.palette.colors[0].b = background.b
+
+        return pygame.surface.Surface(surf=surf)
+
+    def size(self, text):
+        '''Determine the amount of space needed to render text.
+
+        Returns the dimensions needed to render the text. This can be used to
+        help determine the positioning needed for text before it is rendered.
+        It can also be used for wordwrapping and other layout effects.
+
+        Be aware that most fonts use kerning which adjusts the widths for
+        specific letter pairs. For example, the width for "T." will not always
+        match the width for "T" + ".".
+        
+        :Parameters:
+            `text` : str or unicode
+                Text to measure
+
+        :rtype: int, int
+        :return: width, height
+        '''
+        return TTF_SizeText(self._font, text)
+
+    def set_underline(self, underline):
+        '''Control if text is rendered with an underline.
+
+        When enabled, all rendered fonts will include an underline. The
+        underline is always one pixel thick, regardless of font size. This can
+        be mixed with the bold and italic modes.
+        
+        :Parameters:
+            `underline` : bool
+                If True, the text will be rendered with an underline.
+
+        '''
+        style = TTF_GetFontStyle(self._font)
+        if underline:
+            style |= TTF_STYLE_UNDERLINE
+        else:
+            style &= ~TTF_STYLE_UNDERLINE
+        TTF_SetFontStyle(self._font, style)
+
+    def get_underline(self):
+        '''Check if text will be rendered with an underline.
+
+        :rtype: bool
+        '''
+        return TTF_GetFontStyle(self._font) & TTF_STYLE_UNDERLINE != 0
+
+    def set_bold(self, bold):
+        '''Enable fake rendering of bold text.
+
+        Enables the bold rendering of text. This is a fake stretching of the
+        font that doesn't look good on many font types. If possible load the
+        font from a real bold font file. While bold, the font will have a
+        different width than when normal. This can be mixed with the italic
+        and underline modes.
+        
+        :Parameters:
+            `bold` : bool
+                If True, the text will be rendered with a heavier pen.
+
+        '''
+        style = TTF_GetFontStyle(self._font)
+        if bold:
+            style |= TTF_STYLE_BOLD
+        else:
+            style &= ~TTF_STYLE_BOLD
+        TTF_SetFontStyle(self._font, style)
+
+    def get_bold(self):
+        '''Check if text will be rendered bold.
+
+        :rtype: bool
+        '''
+        return TTF_GetFontStyle(self._font) & TTF_STYLE_BOLD != 0
+
+    def set_italic(self):
+        '''Enable fake rendering of italic text.
+
+        Enables fake rendering of italic text. This is a fake skewing of the
+        font that doesn't look good on many font types. If possible load the
+        font from a real italic font file. While italic the font will have a
+        different width than when normal. This can be mixed with the bold and
+        underline modes.
+        
+        :Parameters:
+            `italic` : bool
+                If True, the text will be rendered at an oblique angle.
+
+        '''
+        style = TTF_GetFontStyle(self._font)
+        if italic:
+            style |= TTF_STYLE_ITALIC
+        else:
+            style &= ~TTF_STYLE_ITALIC
+        TTF_SetFontStyle(self._font, style)
+
+    def get_italic(self):
+        '''Check if the text will be rendered italic.
+
+        :rtype: bool 
+        '''
+        return TTF_GetFontStyle(self._font) & TTF_STYLE_ITALIC != 0
+
+    def get_linesize(self):
+        '''Get the line space of the font text.
+
+        Return the height in pixels for a line of text with the font. When
+        rendering multiple lines of text this is the recommended amount of
+        space between lines.
+
+        :rtype: int
+        '''
+        return TTF_FontLineSkip(self._font)
+
+    def get_height(self):
+        '''Get the height of the font.
+
+        Return the height in pixels of the actual rendered text. This is the
+        average size for each glyph in the font.
+
+        :rtype: int
+        '''
+        return TTF_FontHeight(self._font)
+
+    def get_ascent(self):
+        '''Get the ascent of the font.
+
+        Return the height in pixels for the font ascent. The ascent is the
+        number of pixels from the font baseline to the top of the font.
+        
+        :rtype: int
+        '''
+        return TTF_FontAscent(self._font)
+
+    def get_descent(self):
+        '''Get the descent of the font.
+
+        Return the height in pixels for the font descent. The descent is the
+        number of pixels from the font baseline to the bottom of the font.
+        
+        :rtype: int
+        '''
+        return TTF_FontDescent(self._font)
+
+FontType = Font
         if fileext == '.bmp':
             SDL_SaveBMP(surf, file)
         elif fileext in ('.jpg', '.jpeg'):
-            raise pygame.base.error, 'No support for jpg compiled in.'
+            raise pygame.base.error, 'No support for jpg compiled in.' # TODO
         elif fileext == '.png':
-            raise pygame.base.error, 'No support for png compiled in.'
+            raise pygame.base.error, 'No support for png compiled in.' # TODO
         else:
             raise NotImplementedError, 'TODO: TGA support'
     
         if len(args) == 4:
             if args[2] < 0 or args[3] < 0:
                 raise NotImplementedError, 'Negative sized rect not permitted'
-            object.__setattr__(self, '_r', SDL.SDL_Rect(*args))
+            object.__setattr__(self, '_r', SDL.SDL_Rect(int(args[0]),
+                                                        int(args[1]),
+                                                        int(args[2]),
+                                                        int(args[3])))
         elif len(args) == 2:
             if args[1][0] < 0 or args[1][1] < 0:
                 raise NotImplementedError, 'Negative sized rect not permitted'
             object.__setattr__(self, '_r', 
-                               SDL.SDL_Rect(args[0][0], args[0][1], 
-                                            args[1][0], args[1][1]))
+                               SDL.SDL_Rect(int(args[0][0]), int(args[0][1]), 
+                                            int(args[1][0]), int(args[1][1])))
         else:
             raise TypeError, 'Argument must be rect style object'
 

pygame/surface.py

             destpos = pygame.rect._rect_from_object(destpos)[:2]
         except:
             pass
-        destrect.x = destpos[0]
-        destrect.y = destpos[1]
+        destrect.x = int(destpos[0])
+        destrect.y = int(destpos[1])
         _surface_blit(self, source, destrect, sourcerect, special_flags)
         return pygame.rect.Rect(destrect)
 
             newsurf = SDL_ConvertSurface(surf, format, flags)
 
         self._unprep()
-        return Surface(newsurf)
+        return Surface(surf=newsurf)
 
     def convert_alpha(self, surface=None):
         '''Create a copy of a surface with the desired pixel format,

pygame/sysfont.py

+##    pygame - Python Game Library
+##    Copyright (C) 2000-2003  Pete Shinners
+##
+##    This library is free software; you can redistribute it and/or
+##    modify it under the terms of the GNU Library General Public
+##    License as published by the Free Software Foundation; either
+##    version 2 of the License, or (at your option) any later version.
+##
+##    This library is distributed in the hope that it will be useful,
+##    but WITHOUT ANY WARRANTY; without even the implied warranty of
+##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+##    Library General Public License for more details.
+##
+##    You should have received a copy of the GNU Library General Public
+##    License along with this library; if not, write to the Free
+##    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+##
+##    Pete Shinners
+##    pete@shinners.org
+
+"sysfont, used in the font module to find system fonts"
+
+import os, sys
+
+
+#create simple version of the font name
+def _simplename(name):
+    for char in '_ -':
+        name = name.replace(char, '')
+    name = name.lower()
+    name = name.replace('-', '')
+    name = name.replace("'", '')
+    return name
+
+
+#insert a font and style into the font dictionary
+def _addfont(name, bold, italic, font, fontdict):
+    if not fontdict.has_key(name):
+        fontdict[name] = {}
+    fontdict[name][bold, italic] = font
+
+
+#read the fonts on windows
+def initsysfonts_win32():
+    import _winreg
+    fonts = {}
+    mods = 'demibold', 'narrow', 'light', 'unicode', 'bt', 'mt'
+    fontdir = os.path.join(os.environ['WINDIR'], "Fonts")
+
+    #this is a list of registry keys containing information
+    #about fonts installed on the system.
+    keys = []
+
+    #find valid registry keys containing font information.
+    possible_keys = [
+        r"SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts",
+        r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
+        ]
+
+    for key_name in possible_keys:
+        try:
+            key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name)
+            keys.append(key)
+        except WindowsError:
+            pass
+
+    for key in keys:
+        fontdict = {}
+        for i in range(_winreg.QueryInfoKey(key)[1]):
+            try: name, font, t = _winreg.EnumValue(key,i)
+            except EnvironmentError: break
+
+            # try and handle windows unicode strings for some file names.
+            
+            # here are two documents with some information about it:
+            # http://www.python.org/peps/pep-0277.html
+            # https://www.microsoft.com/technet/archive/interopmigration/linux/mvc/lintowin.mspx#ECAA
+            try:
+                font = str(font)
+            except UnicodeEncodeError:
+                # MBCS is the windows encoding for unicode file names.
+                try:
+                    font = font.encode('MBCS')
+                except:
+                    # no goodness with str or MBCS encoding... skip this font.
+                    continue
+   
+            if font[-4:].lower() not in [".ttf", ".ttc"]:
+                continue
+            if os.sep not in font:
+                font = os.path.join(fontdir, font)
+
+            if name[-10:] == '(TrueType)':
+                name = name[:-11]
+            name = name.lower().split()
+
+            bold = italic = 0
+            for m in mods:
+                if m in name:
+                    name.remove(m)
+            if 'bold' in name:
+                name.remove('bold')
+                bold = 1
+            if 'italic' in name:
+                name.remove('italic')
+                italic = 1
+            name = ''.join(name)
+
+            name=_simplename(name)
+
+            _addfont(name, bold, italic, font, fonts)
+    return fonts
+
+
+#read of the fonts on osx (fill me in!)
+def initsysfonts_darwin():
+    paths = ['/Library/Fonts',
+             '~/Library/Fonts',
+             '/Local/Library/Fonts',
+             '/Network/Library/Fonts']
+    fonts = {}
+    for p in paths:
+        if os.path.isdir(p):
+            pass
+            #os.path.walk(p, _fontwalk, fonts)
+    return fonts
+
+
+
+
+#read the fonts on unix
+def initsysfonts_unix():
+    fonts = {}
+
+    # we use the fc-list from fontconfig to get a list of fonts.
+
+    try:
+        # note, we use popen3 for if fc-list isn't there to stop stderr printing.
+        flin, flout, flerr = os.popen3('fc-list : file family style')
+    except:
+        return fonts
+
+    try:
+        for line in flout:
+            try:
+                filename, family, style = line.split(':', 2)
+                if filename[-4:].lower() in ['.ttf', '.ttc']:
+                    bold = style.find('Bold') >= 0
+                    italic = style.find('Italic') >= 0
+                    oblique = style.find('Oblique') >= 0
+                    _addfont(_simplename(family), bold, italic or oblique, filename, fonts)
+            except:
+                # try the next one.
+                pass
+    except:
+        pass
+
+    return fonts
+
+
+
+#create alias entries
+def create_aliases():
+    aliases = (
+        ('monospace', 'misc-fixed', 'courier', 'couriernew', 'console',
+                'fixed', 'mono', 'freemono', 'bitstreamverasansmono',
+                'verasansmono', 'monotype', 'lucidaconsole'),
+        ('sans', 'arial', 'helvetica', 'swiss', 'freesans',
+                'bitstreamverasans', 'verasans', 'verdana', 'tahoma'),
+        ('serif', 'times', 'freeserif', 'bitstreamveraserif', 'roman',
+                'timesroman', 'timesnewroman', 'dutch', 'veraserif',
+                'georgia'),
+        ('wingdings', 'wingbats'),
+    )
+    for set in aliases:
+        found = None
+        fname = None
+        for name in set:
+            if Sysfonts.has_key(name):
+                found = Sysfonts[name]
+                fname = name
+                break
+        if not found:
+            continue
+        for name in set:
+            if not Sysfonts.has_key(name):
+                Sysalias[name] = found
+
+
+Sysfonts = {}
+Sysalias = {}
+
+#initialize it all, called once
+def initsysfonts():
+    if sys.platform == 'win32':
+        fonts = initsysfonts_win32()
+    elif sys.platform == 'darwin':
+        fonts = initsysfonts_darwin()
+    else:
+        fonts = initsysfonts_unix()
+    Sysfonts.update(fonts)
+    create_aliases()
+    if not Sysfonts: #dummy so we don't try to reinit
+        Sysfonts[None] = None
+
+
+
+#the exported functions
+
+def SysFont(name, size, bold=False, italic=False):
+    '''Create a Font object from the system fonts.
+
+    Return a new Font object that is loaded from the system fonts. The font will
+    match the requested bold and italic flags. If a suitable system font is not
+    found this will fallback on loading the default pygame font. The font name
+    can be a comma separated list of font names to look for.
+    
+    :Parameters:
+        `name` : str
+            Font family or comma-separated list of families.
+        `size` : int
+            Size of font, in points.
+        `bold` : bool
+            True if boldface variant requested.
+        `italic` : bool
+            True if italic variant requested.
+
+    :rtype: `Font`
+    '''
+    import pygame.font
+
+    if not Sysfonts:
+        initsysfonts()
+    
+    gotbold = gotitalic = False
+    fontname = None
+    if name:
+        allnames = name
+        for name in allnames.split(','):
+            name = _simplename(name)
+            styles = Sysfonts.get(name)
+            if not styles:
+                styles = Sysalias.get(name)
+            if styles:
+                while not fontname:
+                    plainname = styles.get((False, False))
+                    fontname = styles.get((bold, italic))
+                    if plainname != fontname:
+                        gotbold = bold
+                        gotitalic = italic
+                    elif not fontname:
+                        fontname = plainname
+            if fontname: break
+
+    font = pygame.font.Font(fontname, size)
+    if bold and not gotbold:
+        font.set_bold(1)
+    if italic and not gotitalic:
+        font.set_italic(1)
+
+    return font
+
+
+def get_fonts():
+    '''Get all available fonts.
+
+    Returns a list of all the fonts available on the system. The names of the
+    fonts will be set to lowercase with all spaces and punctuation removed.
+    This works on most systems, but some will return an empty list if they
+    cannot find fonts.  
+
+    :rtype: list of str
+    '''
+
+    if not Sysfonts:
+        initsysfonts()
+    return Sysfonts.keys()
+
+
+def match_font(name, bold=0, italic=0):
+    '''Find a specific font on the system.
+
+    Returns the full path to a font file on the system. If bold or italic are
+    set to true, this will attempt to find the correct family of font.
+
+    The font name can actually be a comma separated list of font names to try.
+    If none of the given names are found, None is returned.
+
+    Example::
+
+        >>> print pygame.font.match_font('bitstreamverasans')
+        '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf'
+    
+    :Parameters:
+        `name` : str
+            Font family or comma-separated list of families.
+        `bold` : bool
+            True if boldface variant requested.
+        `italic` : bool
+            True if italic variant requested.
+
+    :rtype: str
+    '''
+    if not Sysfonts:
+        initsysfonts()
+
+    fontname = None
+    allnames = name
+    for name in allnames.split(','):
+        name = _simplename(name)
+        styles = Sysfonts.get(name)
+        if not styles:
+            styles = Sysalias.get(name)
+        if styles:
+            while not fontname:
+                fontname = styles.get((bold, italic))
+                if italic:
+                    italic = 0
+                elif bold:
+                    bold = 0
+                elif not fontname:
+                    fontname = styles.values()[0]
+        if fontname: break
+    return fontname
+
+