1

I have a text function that render text.The function is the following

def textFunc(font,msg,color,x,y,center):
    text_render = font.render(msg,True,color)
    text_rect = text_render.get_rect()
    
    #If center is true, then the X,Y will be used as the center
    if center == True:
        text_rect.center = (x,y)
    else:
        text_rect = (x,y)
    game_display.blit(text_render,text_rect)

However is my msg string is too long it will render outside the window.

Is there a way for my text to register that is too long for my window and so continues below the start of the text? Similarly to how computers do it automaticall

Sort of like so: Text

Peter Petigru
  • 207
  • 2
  • 9

2 Answers2

1

There is no automatic solution. You have to implement the text wrap by yourself and draw the text line by line respectively word by word.
Fortunately PyGame wiki provides a function that for this task. See PyGame wiki Simple Text Wrapping for pygame.

I've extended the function and added an additional argument, which provides left or right aligned text, centered text or even block mode:

textAlignLeft = 0
textAlignRight = 1
textAlignCenter = 2
textAlignBlock = 3

def drawText(surface, text, color, rect, font, align=textAlignLeft, aa=False, bkg=None):
    lineSpacing = -2
    spaceWidth, fontHeight = font.size(" ")[0], font.size("Tg")[1]

    listOfWords = text.split(" ")
    if bkg:
        imageList = [font.render(word, 1, color, bkg) for word in listOfWords]
        for image in imageList: image.set_colorkey(bkg)
    else:
        imageList = [font.render(word, aa, color) for word in listOfWords]

    maxLen = rect[2]
    lineLenList = [0]
    lineList = [[]]
    for image in imageList:
        width = image.get_width()
        lineLen = lineLenList[-1] + len(lineList[-1]) * spaceWidth + width
        if len(lineList[-1]) == 0 or lineLen <= maxLen:
            lineLenList[-1] += width
            lineList[-1].append(image)
        else:
            lineLenList.append(width)
            lineList.append([image])

    lineBottom = rect[1]
    lastLine = 0
    for lineLen, lineImages in zip(lineLenList, lineList):
        lineLeft = rect[0]
        if align == textAlignRight:
            lineLeft += + rect[2] - lineLen - spaceWidth * (len(lineImages)-1)
        elif align == textAlignCenter:
            lineLeft += (rect[2] - lineLen - spaceWidth * (len(lineImages)-1)) // 2
        elif align == textAlignBlock and len(lineImages) > 1:
            spaceWidth = (rect[2] - lineLen) // (len(lineImages)-1)
        if lineBottom + fontHeight > rect[1] + rect[3]:
            break
        lastLine += 1
        for i, image in enumerate(lineImages):
            x, y = lineLeft + i*spaceWidth, lineBottom
            surface.blit(image, (round(x), y))
            lineLeft += image.get_width() 
        lineBottom += fontHeight + lineSpacing

    if lastLine < len(lineList):
        drawWords = sum([len(lineList[i]) for i in range(lastLine)])
        remainingText = ""
        for text in listOfWords[drawWords:]: remainingText += text + " "
        return remainingText
    return ""

Minimal example: repl.it/@Rabbid76/PyGame-TextWrap

import pygame

pygame.init()
font = pygame.font.SysFont(None, 40)

msg = "Simple function that will draw text and wrap it to fit the rect passed.  If there is any text that will not fit into the box, the remaining text will be returned."
textRect = pygame.Rect(100, 100, 300, 300)

window = pygame.display.set_mode((500, 500))
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill((255, 255, 255))
    pygame.draw.rect(window, (0, 0, 0), textRect, 1)
    drawTextRect = textRect.inflate(-5, -5)
    drawText(window, msg, (0, 0, 0), drawTextRect, font, textAlignBlock, True)
    pygame.display.flip()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
0

As of pygame-ce (a modern fork of pygame) version 2.1.4 pygame.Font.render now has newline ("\n") and wraplength (the width in pixels before wrapping to a new line) support.

So, you just pass the text you want to pygame.Font.render and specify the window width as the last argument so that it wraps once the text reaches the width of the window. Since this method doesn't yet accept keyword arguments you must provide the background color argument too, it can be None or you can provide a color value with an alpha value of 0.

Here is a short code example:

import pygame


WIDTH, HEIGHT = 640, 360
FPS = 60

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()

font = pygame.Font(None, 32)
text_surf = font.render(
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
    "In feugiat imperdiet velit, et dictum arcu malesuada in.",
    True,
    "black",
    None,  # or (0, 0, 0, 0) for example
    WIDTH,
)

running = True
while running:
    clock.tick(FPS)
    screen.fill("white")

    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            running = False

    screen.blit(text_surf, (0, 0))

    pygame.display.flip()

And the result:

enter image description here

Additionally to install the latest version of pygame-ce simply follow these steps:

  1. pip uninstall pygame (to avoid conflict, this has to be done only if you have pygame installed already)
  2. pip install pygame-ce
  3. Enjoy the new and shiny features

More info about the fork can be found here: Pygame: Community Edition Announcement You can also join the official discord server for pygame(-ce): https://discord.gg/pygame

Matiiss
  • 5,970
  • 2
  • 12
  • 29