Use fractional coordinates when drawing text
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 807 B After Width: | Height: | Size: 809 B |
|
@ -1238,6 +1238,27 @@ def test_stroke_descender():
|
|||
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76)
|
||||
|
||||
|
||||
@skip_unless_feature("freetype2")
|
||||
def test_split_word():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (230, 55))
|
||||
expected = im.copy()
|
||||
expected_draw = ImageDraw.Draw(expected)
|
||||
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 48)
|
||||
expected_draw.text((0, 0), "paradise", font=font)
|
||||
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.text((0, 0), "par", font=font)
|
||||
|
||||
length = draw.textlength("par", font=font)
|
||||
draw.text((length, 0), "adise", font=font)
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, expected)
|
||||
|
||||
|
||||
@skip_unless_feature("freetype2")
|
||||
def test_stroke_multiline():
|
||||
# Arrange
|
||||
|
|
|
@ -452,7 +452,11 @@ class ImageDraw:
|
|||
mode = self.fontmode
|
||||
if stroke_width == 0 and embedded_color:
|
||||
mode = "RGBA"
|
||||
coord = xy
|
||||
coord = []
|
||||
start = []
|
||||
for i in range(2):
|
||||
coord.append(int(xy[i]))
|
||||
start.append(math.modf(xy[i])[0])
|
||||
try:
|
||||
mask, offset = font.getmask2(
|
||||
text,
|
||||
|
@ -463,6 +467,7 @@ class ImageDraw:
|
|||
stroke_width=stroke_width,
|
||||
anchor=anchor,
|
||||
ink=ink,
|
||||
start=start,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
@ -478,6 +483,7 @@ class ImageDraw:
|
|||
stroke_width,
|
||||
anchor,
|
||||
ink,
|
||||
start=start,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
@ -490,7 +496,7 @@ class ImageDraw:
|
|||
# extract mask and set text alpha
|
||||
color, mask = mask, mask.getband(3)
|
||||
color.fillband(3, (ink >> 24) & 0xFF)
|
||||
x, y = (int(c) for c in coord)
|
||||
x, y = coord
|
||||
self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask)
|
||||
else:
|
||||
self.draw.draw_bitmap(coord, mask, ink)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#
|
||||
|
||||
import base64
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
@ -588,6 +589,7 @@ class FreeTypeFont:
|
|||
stroke_width=0,
|
||||
anchor=None,
|
||||
ink=0,
|
||||
start=None,
|
||||
):
|
||||
"""
|
||||
Create a bitmap for the text.
|
||||
|
@ -659,6 +661,7 @@ class FreeTypeFont:
|
|||
stroke_width=stroke_width,
|
||||
anchor=anchor,
|
||||
ink=ink,
|
||||
start=start,
|
||||
)[0]
|
||||
|
||||
def getmask2(
|
||||
|
@ -672,6 +675,7 @@ class FreeTypeFont:
|
|||
stroke_width=0,
|
||||
anchor=None,
|
||||
ink=0,
|
||||
start=None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
|
@ -750,12 +754,23 @@ class FreeTypeFont:
|
|||
size, offset = self.font.getsize(
|
||||
text, mode, direction, features, language, anchor
|
||||
)
|
||||
size = size[0] + stroke_width * 2, size[1] + stroke_width * 2
|
||||
if start is None:
|
||||
start = (0, 0)
|
||||
size = tuple(math.ceil(size[i] + stroke_width * 2 + start[i]) for i in range(2))
|
||||
offset = offset[0] - stroke_width, offset[1] - stroke_width
|
||||
Image._decompression_bomb_check(size)
|
||||
im = fill("RGBA" if mode == "RGBA" else "L", size, 0)
|
||||
self.font.render(
|
||||
text, im.id, mode, direction, features, language, stroke_width, ink
|
||||
text,
|
||||
im.id,
|
||||
mode,
|
||||
direction,
|
||||
features,
|
||||
language,
|
||||
stroke_width,
|
||||
ink,
|
||||
start[0],
|
||||
start[1],
|
||||
)
|
||||
return im, offset
|
||||
|
||||
|
|
|
@ -777,13 +777,15 @@ font_render(FontObject *self, PyObject *args) {
|
|||
const char *lang = NULL;
|
||||
PyObject *features = Py_None;
|
||||
PyObject *string;
|
||||
float x_start = 0;
|
||||
float y_start = 0;
|
||||
|
||||
/* render string into given buffer (the buffer *must* have
|
||||
the right size, or this will crash) */
|
||||
|
||||
if (!PyArg_ParseTuple(
|
||||
args,
|
||||
"On|zzOziL:render",
|
||||
"On|zzOziLff:render",
|
||||
&string,
|
||||
&id,
|
||||
&mode,
|
||||
|
@ -791,7 +793,9 @@ font_render(FontObject *self, PyObject *args) {
|
|||
&features,
|
||||
&lang,
|
||||
&stroke_width,
|
||||
&foreground_ink_long)) {
|
||||
&foreground_ink_long,
|
||||
&x_start,
|
||||
&y_start)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -876,8 +880,8 @@ font_render(FontObject *self, PyObject *args) {
|
|||
}
|
||||
|
||||
/* set pen position to text origin */
|
||||
x = (-x_min + stroke_width) << 6;
|
||||
y = (-y_max + (-stroke_width)) << 6;
|
||||
x = (-x_min + stroke_width + x_start) * 64;
|
||||
y = (-y_max + (-stroke_width) - y_start) * 64;
|
||||
|
||||
if (stroker == NULL) {
|
||||
load_flags |= FT_LOAD_RENDER;
|
||||
|
|