[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Re: BUG: import font fails with python2.3 cvs 1.7.2pre 2006/01/13



On Fri, Jan 13, 2006 at 11:34:16AM +1100, Rene Dudfield wrote:
> I tried changing PyUnicode_AsEncodedObject to PyUnicode_AsEncodedString.
> 
> I think it works... but haven't really tested it fully.  I think this
> should be ok.
> 
> If someone is using unicode fonts, that can test pygame cvs please do!
>  Also a little example that tests unicode fonts would be very handy.

Here's a little app (attached, unless I forget) that lets you select any
of the system fonts returned by pygame.fonts.get_fonts, and display the
first 1023 Unicode characters in that font.

It is very easy to crash the application by just moving around and
rendering different fonts.  There appears to be some memory corruption
going on, as the C library complains about its malloc arena being
corrupted:

  *** glibc detected *** free(): invalid next size (fast): 0x08254c28 ***
  Aborted

The crashes are not very deterministic: if the first font I choose to
render is 'arial', I get the crash right away, but if I render a couple
of other fonts first, I can then 'render' arial just fine, and get a
crash some time later.

I can crash both PyGame 1.6.2 from Ubuntu, and 1.7.2pre that I checked
out from CVS today.


Another, smaller problem is that not all of the fonts returned by
pygame.fonts.get_fonts() can be actually used.  pygame.fonts.match_font(
fontname) returns None for those fonts.  It seems that all of the
unavailable fonts have commas in their names (e.g., "padmaa,padmmaa").
fc-list mentions them like this:

  /usr/share/fonts/truetype/ttf-gujarati-fonts/padmaa-Medium-0.5.ttf: padmaa,padmmaa:style=regular,Medium
  /usr/share/fonts/truetype/ttf-gujarati-fonts/padmaa-Bold-0.5.ttf: padmaa,padmmaa:style=Bold,medium

Marius Gedminas
-- 
"What's the name of the new OO COBOL -- an equivalent of C++?"
"ADD 1 TO COBOL GIVING COBOL"
#!/usr/bin/env pytho
"""
System font browser for PyGame, version 0.1.

On the left you see a list of all system fonts found by PyGame.  Select
a font using arrow keys, then press Enter to see a list of characters
(from the Unicode range U+0001 to U+0400) on the right.  Alphanumeric
characters are shown brighter than punctuation or control characters.

Esc quits.

This appliction seems to be just the things to identify bugs in PyGame (or
maybe SDL_ttf): rendering some fonts on my system apparently overwrites random
memory locations and causes the application to crash with messages like

  *** glibc detected *** free(): invalid next size (fast): 0x08254358 ***
  Aborted

Written by Marius Gedminas <mgedmin@xxxxxxxx>.  This program can be distributed
with the same licence as PyGame itself, which is the GNU LGPL 2.1.
"""

import sys
import pygame
from pygame.locals import *

MODE = (800, 600)


class ListBox:
    bgcolor = (0, 0, 0)
    fgcolor = (255, 255, 255)
    selected_bgcolor = (127, 127, 255)
    selected_fgcolor = (255, 255, 255)
    padding = (2, 1)

    def __init__(self, rect, font, items):
        self.rect = rect
        self.font = font
        self.items = items
        self.top = 0
        self.selected = 0
        self.itemheight = self.font.get_linesize() + 2 * self.padding[1]
        self.pagesize = max(1, self.rect.height / self.itemheight)
        self.buf = pygame.Surface(self.rect.size)
        self.render()

    def render(self):
        self.buf.fill(self.bgcolor)
        x, y = self.padding
        for n in range(self.top, len(self.items)):
            item = self.items[n]
            if n == self.selected:
                self.buf.fill(self.selected_bgcolor,
                              (x - self.padding[0], y - self.padding[1],
                               self.buf.get_width(), self.itemheight))
                img = self.font.render(item, True, self.selected_fgcolor)
            else:
                img = self.font.render(item, True, self.fgcolor)
            self.buf.blit(img, (x, y))
            y += self.itemheight
            if y >= self.buf.get_height():
                break

    def draw(self, surface):
        surface.blit(self.buf, self.rect)

    def select_next(self):
        if self.selected + 1 < len(self.items):
            self.selected += 1
        if self.selected >= self.top + self.pagesize:
            self.top = self.selected - self.pagesize + 1
        self.render()

    def select_prev(self):
        if self.selected > 0:
            self.selected -= 1
        if self.selected < self.top:
            self.top = self.selected
        self.render()

    def select_first(self):
        self.selected = 0
        self.top = 0
        self.render()

    def select_last(self):
        self.selected = len(self.items) - 1
        self.top = max(0, self.selected - self.pagesize + 1)
        self.render()

    def page_up(self):
        self.selected = max(0, self.selected - self.pagesize + 1)
        self.top = max(0, self.top - self.pagesize + 1)
        self.render()

    def page_down(self):
        self.selected = min(len(self.items) - 1,
                            self.selected + self.pagesize - 1)
        self.top = min(self.selected, self.top + self.pagesize - 1)
        self.render()


class FontBox:
    font_size = 12
    bgcolor = (0, 20, 0)
    fgcolor = (200, 250, 255)
    nonalnum_fgcolor = (100, 150, 155)
    errorcolor = (200, 50, 50)
    padding = (2, 1)

    # Range of characters to display
    minchar = 1
    maxchar = 0x0400-1

    # How many characters to examine to determine bounding box limits
    chars_to_determine_font_size = 254

    def __init__(self, rect, fontname=None):
        self.rect = rect
        self.buf = pygame.Surface(self.rect.size)
        self.set_font(fontname)

    def set_font(self, fontname):
        self.font = None
        self.fontname = fontname
        if self.fontname is not None:
            self.filename = pygame.font.match_font(fontname)
            if self.filename:
                # Intentionally not using SysFont here: I want no fallback
                self.font = pygame.font.Font(self.filename, self.font_size)
        if self.font is not None:
            self.itemheight = self.font.get_linesize() + 2 * self.padding[1]
            self.itemwidth = (max([self.font.size(unichr(self.minchar + c))[0]
                                   for c in range(self.chars_to_determine_font_size)])
                              + 2 * self.padding[0])
        self.render()

    def render(self):
        self.buf.fill(self.bgcolor)
        if self.fontname is None:
            return
        if self.font is None:
            return
        width = self.rect.width - 2 * self.padding[0]
        items_per_row = max(width / self.itemwidth, 1)
        origin_x = self.padding[0] + width % self.itemwidth / 2
        innerwidth = self.itemwidth - 2 * self.padding[0]
        for row, y in enumerate(range(self.padding[1], self.rect.height,
                                      self.itemheight)):
            x = origin_x
            for col in range(items_per_row):
                chr = unichr(row * items_per_row + col)
                if self.minchar <= ord(chr) <= self.maxchar:
                    if chr.isalnum():
                        color = self.fgcolor
                    else:
                        color = self.nonalnum_fgcolor
                    try:
                        img = self.font.render(chr, True, color)
                    except pygame.error:
                        self.buf.fill(self.errorcolor,
                                      (x, y,
                                       self.itemwidth - self.padding[0] * 2,
                                       self.itemheight - self.padding[1] * 2))
                    else:
                        self.buf.blit(img,
                                      (x + (innerwidth - img.get_width()) / 2,
                                       y))
                x += self.itemwidth

    def draw(self, surface):
        surface.blit(self.buf, self.rect)


class FontBrowser:

    default_font = 'Verdana'

    def __init__(self, screen):
        self.screen = screen
        default_font = pygame.font.SysFont(self.default_font, 24)
        fontnames = pygame.font.get_fonts()
        fontnames.sort()
        rect = screen.get_rect()
        rect.width /= 2
        rect.inflate_ip(-8, -8)
        self.fonts = ListBox(rect, default_font, fontnames)
        rect = rect.move(screen.get_width() / 2, 0)
        self.chars = FontBox(rect)
        self.draw()
        self.draw_chars()

    def draw(self):
        self.fonts.draw(self.screen)
        pygame.display.update(self.fonts.rect)

    def draw_chars(self):
        self.chars.draw(self.screen)
        pygame.display.update(self.chars.rect)

    def process_events(self, events):
        for e in events:
            if e.type == QUIT:
                sys.exit(0)
            if e.type == KEYDOWN:
                if e.key == K_ESCAPE:
                    sys.exit(0)
                elif e.key == K_UP:
                    self.fonts.select_prev()
                    self.draw()
                elif e.key == K_DOWN:
                    self.fonts.select_next()
                    self.draw()
                elif e.key == K_HOME:
                    self.fonts.select_first()
                    self.draw()
                elif e.key == K_END:
                    self.fonts.select_last()
                    self.draw()
                elif e.key == K_PAGEUP:
                    self.fonts.page_up()
                    self.draw()
                elif e.key == K_PAGEDOWN:
                    self.fonts.page_down()
                    self.draw()
                elif e.key == K_RETURN:
                    self.chars.set_font(self.fonts.items[self.fonts.selected])
                    self.draw_chars()


def main():
    pygame.init()
    pygame.display.set_caption("PyGame Font Browser")
    screen = pygame.display.set_mode(MODE)
    pygame.key.set_repeat(250, 1000/30)
    browser = FontBrowser(screen)
    while True:
        browser.process_events([pygame.event.wait()])


if __name__ == '__main__':
    main()

Attachment: signature.asc
Description: Digital signature