web-dev-qa-db-ja.com

PILでテキストを中央揃え/中央揃えにしますか?

PILを使用する場合、テキストを中央揃え(および中央垂直揃え)にするにはどうすればよいですか?

49

使用する - Draw.textsize method テキストサイズを計算し、それに応じて位置を再計算します。

以下に例を示します。

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")

そして結果:

image with centered text

フォントサイズが異なる場合は、次のようなフォントを含めます。

myFont = ImageFont.truetype("my-font.ttf", 16)
draw.textsize(msg, font=myFont)
102
sastanin

次に、textwrapを使用して長い行を断片に分割し、textsizeメソッドを使用して位置を計算するコード例を示します。

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')

enter image description here

56
unutbu

Draw.textsizeメソッドは不正確であることに注意してください。私は低ピクセルの画像を扱っていましたが、いくつかのテストの後、textsizeではすべての文字が6ピクセル幅であると見なされますが、「I」では最大になります。 2ピクセルと「W」は最小かかります。 8ピクセル(私の場合)。そして、私のテキストに応じて、それはまったく中央に置かれた、またはまったく置かれなかった。とはいえ、「6」は平均だったと思うので、長いテキストや大きな画像を扱っているのであれば、それでも大丈夫でしょう。

しかし、今、本当の精度が必要な場合は、使用するフォントオブジェクトのgetsizeメソッドを使用する方が適切です。

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

Edilioのリンクで使用されているとおり。

16
Sindarus

この実装は http://tools.jedutils.com/tools/center-text-image で見つけることができます

自分でルーチンを実装する代わりに、そのページを使用してすぐにイメージを作成できますが、使用されるコードもページに含まれています。

3
edilio

ImageDraw.textのPILドキュメント は開始するのに適した場所ですが、質問には答えないでください。

以下は、画像の中央ではなく、任意の境界ボックスでテキストを中央に配置する方法の例です。境界ボックスは次のように定義されます:(x1, y1) =左上隅と(x2, y2) =右下隅。

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')

出力は、境界ボックスのテキストを垂直方向および水平方向の中央に表示します

PILがalign='center'パラメータ。ただし、複数行テキストのみ用です。メッセージが1行の場合、手動で中央揃えする必要があります。メッセージが複数行の場合、align='center'は後続の行で作業を行いますが、テキストブロックを手動で中央に配置する必要があります。これらのケースは両方とも、上記のコードで一度に解決されます。

2
aaronpenne

textsizeメソッド( docs を参照)を使用して、実際に描画する前にテキストオブジェクトの寸法を把握します。次に、適切な座標から開始して描画します。

2
Arkady