Text mit PIL in der Mitte / Mitte ausrichten?

74

Wie würde ich Text bei Verwendung von PIL mittig ausrichten (und mittig vertikal ausrichten)?

Phillip B. Oldham
quelle

Antworten:

145

Verwenden Sie die Draw.textsizeMethode , um die Textgröße zu berechnen und die Position entsprechend neu zu berechnen.

Hier ist ein Beispiel:

from PIL import Image, ImageDraw

W, H = (300,200)
msg = "hello"

im = Image.new("RGBA",(W,H),"yellow")
draw = ImageDraw.Draw(im)
w, h = draw.textsize(msg)
draw.text(((W-w)/2,(H-h)/2), msg, fill="black")

im.save("hello.png", "PNG")

und das Ergebnis:

Bild mit zentriertem Text

Wenn Ihre Schriftgröße unterschiedlich ist, geben Sie die folgende Schriftart an:

myFont = ImageFont.truetype("my-font.ttf", 16)
draw.textsize(msg, font=myFont)
Sastanin
quelle
6
draw.textsize: (self, text, font = None)
WeizhongTu
25
Wenn Ihre Schriftgröße unterschiedlich ist, ist es wichtig, Ihre Schrift wie draw.textsize(msg, font=myFont)folgt einzuschließen : Andernfalls wird sie nicht richtig zentriert
Coco
1
Wie kann sichergestellt werden, dass der Text nicht aus dem Bild herausläuft?
Dheeraj M Pai
Wie zentriere ich mehrzeilige Texte?
Nagabhushan SN
2
Der obige Code enthält einen Fehler. Es sollte (Ww / 2, Hh / 2) nicht ((Ww) / 2, (Hh) / 2) sein. Versuchte den obigen Weg und fand es.
Ijas
75

Hier ist ein Beispielcode, der Textwrap verwendet, um eine lange Zeile in Teile zu teilen, und dann die textsizeMethode verwendet, um die Positionen zu berechnen.

from PIL import Image, ImageDraw, ImageFont
import textwrap

astr = '''The rain in Spain falls mainly on the plains.'''
para = textwrap.wrap(astr, width=15)

MAX_W, MAX_H = 200, 200
im = Image.new('RGB', (MAX_W, MAX_H), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(
    '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf', 18)

current_h, pad = 50, 10
for line in para:
    w, h = draw.textsize(line, font=font)
    draw.text(((MAX_W - w) / 2, current_h), line, font=font)
    current_h += h + pad

im.save('test.png')

Geben Sie hier die Bildbeschreibung ein

unutbu
quelle
2
Textwrap ist nett, aber denken Sie daran, dass der von Ihnen festgelegte ganzzahlige Haltepunkt widthZeichen zählt, während PIL-Bilder in Pixel messen. Ich habe eine Version des oben genannten verwendet, aber vor der for-Schleife, die die Zeilen schreibt, eine while-Schleife hinzugefügt. Zuerst habe ich eine beliebig hohe Zeichenanzahl festgelegt, um zu beginnen, dann habe ich Textwrap verwendet, um die Linien zu brechen, und dann habe ich .textsize verwendet, um die Pixelbreite der ersten Ausgabe in der Textwrap-Ergebnisliste zu messen. Wenn es passt, fahren Sie fort, andernfalls verringern Sie meine Zeichenanzahl und messen Sie erneut, bis die Linien zum Bild passen.
Chase
25

Man muss beachten, dass die Draw.textsizeMethode ungenau ist. Ich habe mit Bildern mit niedrigen Pixeln gearbeitet, und nach einigen Tests stellte sich heraus, dass textsizejedes Zeichen 6 Pixel breit ist, während ein Zeichen Imax. 2 Pixel und a Wdauert min. 8 Pixel (in meinem Fall). Und so war es, abhängig von meinem Text, überhaupt nicht zentriert. Ich denke, "6" war ein Durchschnitt. Wenn Sie also mit langen Texten und großen Bildern arbeiten, sollte es immer noch in Ordnung sein.

Wenn Sie jedoch eine echte Genauigkeit wünschen, verwenden Sie besser die getsizeMethode des Schriftobjekts, das Sie verwenden möchten :

arial = ImageFont.truetype("arial.ttf", 9)
w,h = arial.getsize(msg)
draw.text(((W-w)/2,(H-h)/2), msg, font=arial, fill="black")

Wie in Edilios Link verwendet.

Sindarus
quelle
1
Nicht die Antwort auf die Frage des OP, aber eine nette, dringend benötigte Funktion. 1+
ddlab
Wichtiger Hinweis: Diese Funktion getsizeakzeptiert nicht-lateinische Zeichen wie € oder deutsche Umlaute. textsizenicht. ThumbsUp :-)
ddlab
6

Die PIL-Dokumente für ImageDraw.text sind ein guter Anfang, aber beantworten Sie Ihre Frage nicht.

Im Folgenden finden Sie ein Beispiel für das Zentrieren des Texts in einem beliebigen Begrenzungsrahmen im Gegensatz zur Bildmitte. Der Begrenzungsrahmen ist definiert als: (x1, y1)= obere linke Ecke und (x2, y2)= untere rechte Ecke.

from PIL import Image, ImageDraw, ImageFont

# Create blank rectangle to write on
image = Image.new('RGB', (300, 300), (63, 63, 63, 0))
draw = ImageDraw.Draw(image)

message = 'Stuck in\nthe middle\nwith you'

bounding_box = [20, 30, 110, 160]
x1, y1, x2, y2 = bounding_box  # For easy reading

font = ImageFont.truetype('Consolas.ttf', size=12)

# Calculate the width and height of the text to be drawn, given font size
w, h = draw.textsize(message, font=font)

# Calculate the mid points and offset by the upper left corner of the bounding box
x = (x2 - x1 - w)/2 + x1
y = (y2 - y1 - h)/2 + y1

# Write the text to the image, where (x,y) is the top left corner of the text
draw.text((x, y), message, align='center', font=font)

# Draw the bounding box to show that this works
draw.rectangle([x1, y1, x2, y2])

image.show()
image.save('text_center_multiline.png')

Die Ausgabe zeigt den vertikal und horizontal zentrierten Text im Begrenzungsrahmen .

Ob Sie eine ein- oder mehrzeilige Nachricht haben, spielt keine Rolle mehr, da PIL den align='center'Parameter integriert hat. Dies gilt jedoch nur für mehrzeiligen Text . Wenn die Nachricht eine einzelne Zeile ist, muss sie manuell zentriert werden. Wenn die Nachricht mehrzeilig ist, align='center'erledigt sie die Arbeit für Sie in nachfolgenden Zeilen, aber Sie müssen den Textblock trotzdem manuell zentrieren. Beide Fälle werden im obigen Code sofort gelöst.

Aaronpenne
quelle
2

Verwenden Sie die textsizeMethode (siehe Dokumente ), um die Abmessungen Ihres Textobjekts zu ermitteln, bevor Sie es tatsächlich zeichnen. Zeichnen Sie es dann beginnend an den entsprechenden Koordinaten.

Arkady
quelle