Skip to content

Commit

Permalink
Merge pull request #6722 from radarhere/font_start
Browse files Browse the repository at this point in the history
Resolves #3977
  • Loading branch information
hugovk committed Nov 16, 2022
2 parents d4c7bd7 + 62db044 commit 2b45623
Show file tree
Hide file tree
Showing 18 changed files with 119 additions and 8 deletions.
Binary file modified Tests/images/test_anchor_multiline_mm_right.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_lm_center.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_lm_left.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_lm_right.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_mm_center.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_mm_left.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_mm_right.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_rm_center.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_rm_left.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/test_combine_multiline_rm_right.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/text_float_coord.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Tests/images/text_float_coord_1_alt.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions Tests/test_imagedraw.py
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions docs/releasenotes/9.4.0.rst
@@ -0,0 +1,54 @@
9.4.0
-----

Backwards Incompatible Changes
==============================

TODO
^^^^

TODO

Deprecations
============

TODO
^^^^

TODO

API Changes
===========

TODO
^^^^

TODO

API Additions
=============

Added start position for getmask and getmask2
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Text may render differently when starting at fractional coordinates, so
:py:meth:`.FreeTypeFont.getmask` and :py:meth:`.FreeTypeFont.getmask2` now
support a ``start`` argument. This tuple of horizontal and vertical offset
will be used internally by :py:meth:`.ImageDraw.text` to more accurately place
text at the ``xy`` coordinates.

Security
========

TODO
^^^^

TODO

Other Changes
=============

TODO
^^^^

TODO
1 change: 1 addition & 0 deletions docs/releasenotes/index.rst
Expand Up @@ -14,6 +14,7 @@ expected to be backported to earlier versions.
.. toctree::
:maxdepth: 2

9.4.0
9.3.0
9.2.0
9.1.1
Expand Down
10 changes: 8 additions & 2 deletions src/PIL/ImageDraw.py
Expand Up @@ -452,7 +452,11 @@ def draw_text(ink, stroke_width=0, stroke_offset=None):
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,
Expand All @@ -463,6 +467,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None):
stroke_width=stroke_width,
anchor=anchor,
ink=ink,
start=start,
*args,
**kwargs,
)
Expand All @@ -478,6 +483,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None):
stroke_width,
anchor,
ink,
start=start,
*args,
**kwargs,
)
Expand All @@ -490,7 +496,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None):
# 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)
Expand Down
29 changes: 27 additions & 2 deletions src/PIL/ImageFont.py
Expand Up @@ -26,6 +26,7 @@
#

import base64
import math
import os
import sys
import warnings
Expand Down Expand Up @@ -588,6 +589,7 @@ def getmask(
stroke_width=0,
anchor=None,
ink=0,
start=None,
):
"""
Create a bitmap for the text.
Expand Down Expand Up @@ -647,6 +649,11 @@ def getmask(
.. versionadded:: 8.0.0
:param start: Tuple of horizontal and vertical offset, as text may render
differently when starting at fractional coordinates.
.. versionadded:: 9.4.0
:return: An internal PIL storage memory instance as defined by the
:py:mod:`PIL.Image.core` interface module.
"""
Expand All @@ -659,6 +666,7 @@ def getmask(
stroke_width=stroke_width,
anchor=anchor,
ink=ink,
start=start,
)[0]

def getmask2(
Expand All @@ -672,6 +680,7 @@ def getmask2(
stroke_width=0,
anchor=None,
ink=0,
start=None,
*args,
**kwargs,
):
Expand Down Expand Up @@ -739,6 +748,11 @@ def getmask2(
.. versionadded:: 8.0.0
:param start: Tuple of horizontal and vertical offset, as text may render
differently when starting at fractional coordinates.
.. versionadded:: 9.4.0
:return: A tuple of an internal PIL storage memory instance as defined by the
:py:mod:`PIL.Image.core` interface module, and the text offset, the
gap between the starting coordinate and the first marking
Expand All @@ -750,12 +764,23 @@ def getmask2(
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

Expand Down
12 changes: 8 additions & 4 deletions src/_imagingft.c
Expand Up @@ -777,21 +777,25 @@ 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,
&dir,
&features,
&lang,
&stroke_width,
&foreground_ink_long)) {
&foreground_ink_long,
&x_start,
&y_start)) {
return NULL;
}

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2b45623

Please sign in to comment.