Title: Word-wrapped text display module
Author: David Clark (da_clark at shaw.ca)
Description: This module contains one function: render_textrect, which will try to return a surface containing the supplied string, word-wrapped as necessary to fit within the supplied rectstyle object. The function can also left or right justify, or center the supplied text. It will raise a TextRectException if the string won't fit into the rect's area.
pygame version required: Any with pygame.font
Comments: This might be good code for a gui's text label widget. On the first pass through, I had code to italicize and bold the supplied text, but I decided I didn't really like the way SDL_ttf did these effects. If you want these text effects, the best way to do it would probably be to find a font that already has a bold or italicized effect. Good ways to improve this function might be to add user-defined padding around the text, allow for vertical centering, and permit a transparent background.
#! /usr/bin/env python class TextRectException: def __init__(self, message = None): self.message = message def __str__(self): return self.message def render_textrect(string, font, rect, text_color, background_color, justification=0): """Returns a surface containing the passed text string, reformatted to fit within the given rect, word-wrapping as necessary. The text will be anti-aliased. Takes the following arguments: string - the text you wish to render. \n begins a new line. font - a Font object rect - a rectstyle giving the size of the surface requested. text_color - a three-byte tuple of the rgb value of the text color. ex (0, 0, 0) = BLACK background_color - a three-byte tuple of the rgb value of the surface. justification - 0 (default) left-justified 1 horizontally centered 2 right-justified Returns the following values: Success - a surface object with the text rendered onto it. Failure - raises a TextRectException if the text won't fit onto the surface. """ import pygame final_lines =  requested_lines = string.splitlines() # Create a series of lines that will fit on the provided # rectangle. for requested_line in requested_lines: if font.size(requested_line) > rect.width: words = requested_line.split(' ') # if any of our words are too long to fit, return. for word in words: if font.size(word) >= rect.width: raise TextRectException, "The word " + word + " is too long to fit in the rect passed." # Start a new line accumulated_line = "" for word in words: test_line = accumulated_line + word + " " # Build the line while the words fit. if font.size(test_line) < rect.width: accumulated_line = test_line else: final_lines.append(accumulated_line) accumulated_line = word + " " final_lines.append(accumulated_line) else: final_lines.append(requested_line) # Let's try to write the text out on the surface. surface = pygame.Surface(rect.size) surface.fill(background_color) accumulated_height = 0 for line in final_lines: if accumulated_height + font.size(line) >= rect.height: raise TextRectException, "Once word-wrapped, the text string was too tall to fit in the rect." if line != "": tempsurface = font.render(line, 1, text_color) if justification == 0: surface.blit(tempsurface, (0, accumulated_height)) elif justification == 1: surface.blit(tempsurface, ((rect.width - tempsurface.get_width()) / 2, accumulated_height)) elif justification == 2: surface.blit(tempsurface, (rect.width - tempsurface.get_width(), accumulated_height)) else: raise TextRectException, "Invalid justification argument: " + str(justification) accumulated_height += font.size(line) return surface if __name__ == '__main__': import pygame import pygame.font from pygame.locals import * pygame.init() display = pygame.display.set_mode((400, 400)) my_font = pygame.font.Font(None, 22) my_string = "Hi there! I'm a nice bit of wordwrapped text. Won't you be my friend? Honestly, wordwrapping is easy, with David's fancy new render_textrect () function.\nThis is a new line.\n\nThis is another one.\n\n\nAnother line, you lucky dog." my_rect = pygame.Rect((40, 40, 300, 300)) rendered_text = render_textrect(my_string, my_font, my_rect, (216, 216, 216), (48, 48, 48), 0) if rendered_text: display.blit(rendered_text, my_rect.topleft) pygame.display.update() while not pygame.event.wait().type in (QUIT, KEYDOWN): pass