mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 09:26:16 +03:00
commit
680aaa41cb
|
@ -256,7 +256,20 @@ class ImageDraw(object):
|
||||||
##
|
##
|
||||||
# Draw text.
|
# Draw text.
|
||||||
|
|
||||||
|
def _multiline_check(self, text):
|
||||||
|
split_character = "\n" if isinstance(text, type("")) else b"\n"
|
||||||
|
|
||||||
|
return split_character in text
|
||||||
|
|
||||||
|
def _multiline_split(self, text):
|
||||||
|
split_character = "\n" if isinstance(text, type("")) else b"\n"
|
||||||
|
|
||||||
|
return text.split(split_character)
|
||||||
|
|
||||||
def text(self, xy, text, fill=None, font=None, anchor=None):
|
def text(self, xy, text, fill=None, font=None, anchor=None):
|
||||||
|
if self._multiline_check(text):
|
||||||
|
return self.multiline_text(xy, text, fill, font, anchor)
|
||||||
|
|
||||||
ink, fill = self._getink(fill)
|
ink, fill = self._getink(fill)
|
||||||
if font is None:
|
if font is None:
|
||||||
font = self.getfont()
|
font = self.getfont()
|
||||||
|
@ -273,14 +286,51 @@ class ImageDraw(object):
|
||||||
mask = font.getmask(text)
|
mask = font.getmask(text)
|
||||||
self.draw.draw_bitmap(xy, mask, ink)
|
self.draw.draw_bitmap(xy, mask, ink)
|
||||||
|
|
||||||
|
def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
|
||||||
|
spacing=0, align="left"):
|
||||||
|
widths, heights = [], []
|
||||||
|
max_width = 0
|
||||||
|
lines = self._multiline_split(text)
|
||||||
|
for line in lines:
|
||||||
|
line_width, line_height = self.textsize(line, font)
|
||||||
|
widths.append(line_width)
|
||||||
|
max_width = max(max_width, line_width)
|
||||||
|
heights.append(line_height)
|
||||||
|
left, top = xy
|
||||||
|
for idx, line in enumerate(lines):
|
||||||
|
if align == "left":
|
||||||
|
pass # left = x
|
||||||
|
elif align == "center":
|
||||||
|
left += (max_width - widths[idx]) / 2.0
|
||||||
|
elif align == "right":
|
||||||
|
left += (max_width - widths[idx])
|
||||||
|
else:
|
||||||
|
assert False, 'align must be "left", "center" or "right"'
|
||||||
|
self.text((left, top), line, fill, font, anchor)
|
||||||
|
top += heights[idx] + spacing
|
||||||
|
left = xy[0]
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get the size of a given string, in pixels.
|
# Get the size of a given string, in pixels.
|
||||||
|
|
||||||
def textsize(self, text, font=None):
|
def textsize(self, text, font=None):
|
||||||
|
if self._multiline_check(text):
|
||||||
|
return self.multiline_textsize(text, font)
|
||||||
|
|
||||||
if font is None:
|
if font is None:
|
||||||
font = self.getfont()
|
font = self.getfont()
|
||||||
return font.getsize(text)
|
return font.getsize(text)
|
||||||
|
|
||||||
|
def multiline_textsize(self, text, font=None, spacing=0):
|
||||||
|
max_width = 0
|
||||||
|
height = 0
|
||||||
|
lines = self._multiline_split(text)
|
||||||
|
for line in lines:
|
||||||
|
line_width, line_height = self.textsize(line, font)
|
||||||
|
height += line_height + spacing
|
||||||
|
max_width = max(max_width, line_width)
|
||||||
|
return max_width, height
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# A simple 2D drawing interface for PIL images.
|
# A simple 2D drawing interface for PIL images.
|
||||||
|
|
BIN
Tests/images/multiline_text_center.png
Normal file
BIN
Tests/images/multiline_text_center.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
Tests/images/multiline_text_right.png
Normal file
BIN
Tests/images/multiline_text_right.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
Tests/images/multiline_text_spacing.png
Normal file
BIN
Tests/images/multiline_text_spacing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -10,6 +10,8 @@ import copy
|
||||||
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
||||||
FONT_SIZE = 20
|
FONT_SIZE = 20
|
||||||
|
|
||||||
|
TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward"
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import ImageFont
|
from PIL import ImageFont
|
||||||
|
@ -134,7 +136,7 @@ try:
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
line_spacing = draw.textsize('A', font=ttf)[1] + 4
|
line_spacing = draw.textsize('A', font=ttf)[1] + 4
|
||||||
lines = ['hey you', 'you are awesome', 'this looks awkward']
|
lines = TEST_TEXT.split("\n")
|
||||||
y = 0
|
y = 0
|
||||||
for line in lines:
|
for line in lines:
|
||||||
draw.text((0, y), line, font=ttf)
|
draw.text((0, y), line, font=ttf)
|
||||||
|
@ -148,6 +150,69 @@ try:
|
||||||
# at epsilon = ~38.
|
# at epsilon = ~38.
|
||||||
self.assert_image_similar(im, target_img, .5)
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_render_multiline_text(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
# Test that text() correctly connects to multiline_text()
|
||||||
|
# and that align defaults to left
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), TEST_TEXT, font=ttf)
|
||||||
|
|
||||||
|
target = 'Tests/images/multiline_text.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
# Test align center and right
|
||||||
|
for align, ext in {"center": "_center",
|
||||||
|
"right": "_right"}.items():
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
|
||||||
|
|
||||||
|
target = 'Tests/images/multiline_text'+ext+'.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_unknown_align(self):
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
# Act/Assert
|
||||||
|
self.assertRaises(AssertionError, lambda: draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align="unknown"))
|
||||||
|
|
||||||
|
def test_multiline_size(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
|
# Test that textsize() correctly connects to multiline_textsize()
|
||||||
|
self.assertEqual(draw.textsize(TEST_TEXT, font=ttf),
|
||||||
|
draw.multiline_textsize(TEST_TEXT, font=ttf))
|
||||||
|
|
||||||
|
def test_multiline_width(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
|
self.assertEqual(draw.textsize("longest line", font=ttf)[0],
|
||||||
|
draw.multiline_textsize("longest line\nline", font=ttf)[0])
|
||||||
|
|
||||||
|
def test_multiline_spacing(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10)
|
||||||
|
|
||||||
|
target = 'Tests/images/multiline_text_spacing.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
def test_rotated_transposed_font(self):
|
def test_rotated_transposed_font(self):
|
||||||
img_grey = Image.new("L", (100, 100))
|
img_grey = Image.new("L", (100, 100))
|
||||||
draw = ImageDraw.Draw(img_grey)
|
draw = ImageDraw.Draw(img_grey)
|
||||||
|
|
|
@ -232,17 +232,39 @@ Methods
|
||||||
Draws the string at the given position.
|
Draws the string at the given position.
|
||||||
|
|
||||||
:param xy: Top left corner of the text.
|
:param xy: Top left corner of the text.
|
||||||
:param text: Text to be drawn.
|
:param text: Text to be drawn. If it contains any newline characters,
|
||||||
|
the text is passed on to mulitiline_text()
|
||||||
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
||||||
:param fill: Color to use for the text.
|
:param fill: Color to use for the text.
|
||||||
|
|
||||||
|
.. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left")
|
||||||
|
|
||||||
|
Draws the string at the given position.
|
||||||
|
|
||||||
|
:param xy: Top left corner of the text.
|
||||||
|
:param text: Text to be drawn. If it contains any newline characters,
|
||||||
|
the text is split and passed on to mulitiline_text()
|
||||||
|
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
||||||
|
:param spacing: The number of pixels between lines.
|
||||||
|
:param align: "left", "center" or "right".
|
||||||
|
|
||||||
.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None)
|
.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None)
|
||||||
|
|
||||||
Return the size of the given string, in pixels.
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
:param text: Text to be measured.
|
:param text: Text to be measured. If it contains any newline characters,
|
||||||
|
the text is passed on to mulitiline_textsize()
|
||||||
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
||||||
|
|
||||||
|
.. py:method:: PIL.ImageDraw.Draw.multiline_textsize(text, font=None, spacing=0)
|
||||||
|
|
||||||
|
Return the size of the given string, in pixels.
|
||||||
|
|
||||||
|
:param text: Text to be measured. If it contains any newline characters,
|
||||||
|
the text is split and passed on to mulitiline_textsize()
|
||||||
|
:param font: An :py:class:`~PIL.ImageFont.ImageFont` instance.
|
||||||
|
:param spacing: The number of pixels between lines.
|
||||||
|
|
||||||
Legacy API
|
Legacy API
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user