From b2301d70d104f36a08ae658f569d02f7796fc8fa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Apr 2023 19:10:51 +1000 Subject: [PATCH 1/6] Removed ImageFont.getsize and related functions --- Tests/images/imagedraw_stroke_multiline.png | Bin 4061 -> 4059 bytes Tests/test_deprecate.py | 19 +- Tests/test_font_pcf.py | 3 - Tests/test_imagedraw.py | 19 +- Tests/test_imagedraw2.py | 16 -- Tests/test_imagefont.py | 95 +--------- docs/deprecations.rst | 98 +++++----- docs/releasenotes/10.0.0.rst | 16 ++ docs/releasenotes/9.2.0.rst | 20 +- src/PIL/ImageDraw.py | 97 +--------- src/PIL/ImageDraw2.py | 16 -- src/PIL/ImageFont.py | 193 -------------------- src/PIL/_deprecate.py | 2 - 13 files changed, 96 insertions(+), 498 deletions(-) diff --git a/Tests/images/imagedraw_stroke_multiline.png b/Tests/images/imagedraw_stroke_multiline.png index fc5e07c8679d5aa12a1d95152ea6d2542f169697..c290fc0568e5c4b40299a208cde7d0d760077fd0 100644 GIT binary patch delta 3993 zcmV;K4`%S)AKM?0B!8kwL_t(|ob8=?d{ouh$G_PLYe*nr5g67Yim3$zw5Y(VmQAee zAk8a9U$Io*zKZa=v~{asZTt2^rEc^?QQ1UMb}WmaBC8-ZASe*FEbIixLXvsjKju!x zGntUg%w3Z4d_U*&nanfyp7Wev?!EKOxz9ZZG#ZUYqtR$I8k4FI8-E&&rUug(UVvS2 z2E?RN3?D$E(_J%}){teMMt0%7%q?NIC}s*E`)uXLtpKNwXADCd@YeiYfK~ zVtwfEAn=OQZaY8>WP`A!r%#T8L>lCSz`sG5K2;HGOHUBUfqz)1owu6-AavTV$nox2 z5ZDZZ0(>gv0oP!sowu6>AiVgbqQ#wSJm(w4z;O^hyG7Yx`cx2D?6mW?(g+HPF5_q` zC76mXgCb}Qz#l-EFk2DJ2)K$PDd4J-2SDh*RuN*x6Ckk2yG;iW>R;qy{XhZNWGCIX zm(M_$J6=(tOMhmC>(NFKW={wdaP4r+cYA4X95R>c`6Srh9R%{!Z@LT+Zu#to9B@4g zLdG0Lg2^*M;Lqxobs-;weVu=p0ap|p0%3h$U+1sH(c}~eq*vJf4G^A~p@_BhCJ@Mt z_<-v+5LzF>9p&r%l6yelt3ccL0-;$BC9#TL*Y0&P;C~8xJgb^y0E~Z|DDm0;nc;Z} zwEY&?OKvk(4N%MxH^NmB?=@(DC*Lu0I)%6^Pzq_4?DZH3LqDt};7YNz2R1Segx;H# zZqJ+y0xK%tD;2H~TS9J{vg3f2Anuy5kZM{`k1E1a%n=uG z#ldM1cBlJ}hzku#EC4sOsdhd0gV1MFWCvV72O)j8(#hEqKwv{K_0%(B+5;UUINEW##L4rSh-U4CTOOYCIH3<-ZA8JdK8!kirAZxoBgyz|l#zbVm^=l9YEmw|( z-+%uO1l|s*!oB_>NCX93J3v@8Lg`!k??B)`LaJ~I2=~6PRJg521O!~yfsk}gxk3Nw zPeI^VsNqT)ln{4?yi`RmV+h}tfXkXVD+6Hk0uqXSHY2=x2izgmnFlKgfbih!O67}2fWVg~_--!^p@1kiM_N}nQ^d5= z@B)|X1`4=tu(oq%U*JbTBDa4+>!Uu~yW=P`H9b4OA&ZCm-X;_?WyVo?--CcU#y$Q%}QRKorB?r~V}~Ypo`=ZG`I}2y6R0 za9BzC*T@xU;#qRXrmt65GZBU}~-T;VYWgnyn}l)f1Gj|(kp^cxUX3{)C@_*IY!312mf)ga88 zr1;%+4~Uh6C;tn=$c0Lyfl~=<5Y3^Kgd)x)`<&jL4g#kj&a!p`B?bsw_caI%HNQbK z;;j21h9S#HKI^j?qfQH<#Do?Ca{|^Dgf(|6Zh!phZy@j&8{Mda4?uY3u|U@gJD+NP3WPE90tH+rt&ClQ zAQ_5@DJ7?w&&88Xi7m1Ho2V6(kyya_M%D&gVO-$HKsjJFiQDn-n12@svKl1xDP5k8&zWhuFfDdI)6`L9iMXtI)cE3 zs&bI6psq3O;g`c@QNVRo;^9%)3_0NT`Qmr~?zhiYKAchlS+ELz9)=rjU>0N=xorW< z)6Xl0_lBz*xFH8VhesgZEOow5qVZ5=#q#;hilJRKr@{#B>%7<1G%7Exk5m$HRVwl_#DT(DsYyR{<#9aY_+k*5V${4*Gw47}{Lce?Djv_CWMI*eq zN@I1)oDxvAuX4AW5T*-Wwqg@ zY2FNx-od-^H3)Zn%$A;EU9)o2m^X&BTo8ENbg}BdCuUqH%1dZ-9(Ff|u1qWcf&^2A zGKhmmU^C<@aaJ&g794?9;oZMz4YOb`TntqpH{`mpPAe0D)gZk7pz~sR%I4nQhYBx+_f(K2 zOavk69J42o-Q39ujmk?wA{lc)ptPpVRvH5zUCnpbVa2ecTj(-GmyvRsjtA(ynaMMm zH-=-^RJ!>KPl13NhF9GQ8;OPoU=w6Rg-MJ|6suy1ax-uRD+dIBesJOCDIib?-JO=G zYFw3z722YN(U1Zi;Wn5C%b?Jxx7Qc!y*7Ye#uP{U!K9~FEW>5CSQSZuDR2yggd+c^ z{soHTx#24ic+sR5t-@u!SRNX{YsP~f55MZK*R~$s`T3ih+NjC6>=MhvNVo#RGtc?! zHDVD6JZnLtrs1-GS1jOeC?m?vhTD}!H}wI5v4B5<&~=ah_hXwZ5crA7 zt=Y90q(wXf+5EdJ>-#C@yVd~EFVHSwP|L+Cg82X++^d+odyZwZ*9ETSVqq*NxxI&C z`_X>D{5h#=zgR~Bj1E?dNkg8Ly4geIr_XFGF8dDk9&*Sr=j z!DX>n$uQZN%nPCaTC&>sk7PalJP4eF7G|_)AujX8iiRX;1L-glX23cqHY#m()PIdh zPnk6d1WMsf3wmcVuFAzSqr{dlco~a_Q5+xO^OvT7dH=F<=G~e&)lamWhWRwQK+<_y zAEo;ay6>RpR#E~*^o_Ax*$o4a~c>=7PhxvN+tY%%YQ3p@d5 zNi0vkNR($5$0<&0QYo&=#R}oziiZZ!6t0DT+h8m_2k*gAU+swnJn)9Hlv{b3J;9qn zIo0%jnu5zzv93aUco;SrxgpVIJp3x>lKl0WJD!+Qa9g|ZXsEdC604k!Fv~~}X?Kv! zF8+Ew`&$q=9sE?zmgBNrEDv3b1tJ=sWlvY7_0kynYyyFQTIoP2xEvA-r~^wtXr9e~ zi57|j2ip-_0%hjpe%IPzw!;PC$h; z{md>PaKWNQD?-ENv{=BsAhbNrl{m$X&ig=27!-_%)3BTmU@s?HvV5>&?!OLzpN3m~ zWQm2bg5Od#wz_o_V8V)Y5hvCvD8GGw@1=!j`2|0FuJ3JL1GT z15lW#*o=gvupdi|II&Uz>RncB#+oXZD{dVe5hqqlfHp@I+u3Bmgxt1>6YCCu8+R$T z1Dyak7;fDWC)RL)fh!f;t{VZ^8*bf^CDy%=PRbdEex#V!^#@Ey*NPyq>cjtk0Zg0j z%~l5B+m7tI5%Nt~fH+dbN`Mcc6+hn1qkmOCd21p73&EsC+04M@+z;vcundHh(|p@O zao|)F>U)-dGW(&Y@NhXKR=TlHWaG1J>)}7L{LtS);G?Rmu@{%^Vzq6vCVI!Qd zd#VDz5?{>3H~lYCo^mFc^xYuvF~peG^&pekVqqx^Fs=EZXqZpaQ#3tAx1ID~N8b%3 zoe!|t=_dSaHD7tIWuu|YZa=-)I3-kQWnX7+dfT^-KY;J_lu%bMZ%*`oCemz!G-s8f z22Ba&S9bn{TQ_>&%!S0NyJ9^xY)Yu&c$N;~uGQZ6c`(XpiF`OZB~)n)`3*SKmbLx) z%YQTcJ(A9OKf4Iip`p|IC{#5ilmq2Uc+95M$lx>oqT!b?9y&mpy}!iE;4++pEZ7fU zz(zQ1qXQa^Mx)VaG#ZVOsu&$;G#ZUYV+sEc80l*p0e;rz00000NkvXXu0mjf*2RjO delta 3990 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 waL7gnG#ZUYqtR$Ik*ODxum}VhG*>` for more information. - - Returns width and height (in pixels) of given text. - - :param text: Text to measure. - - :return: (width, height) - """ - deprecate("getsize", 10, "getbbox or getlength") - return self.font.getsize(text) - def getmask(self, text, mode="", *args, **kwargs): """ Create a bitmap for the text. @@ -398,165 +380,6 @@ def getbbox( width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width return left, top, left + width, top + height - def getsize( - self, - text, - direction=None, - features=None, - language=None, - stroke_width=0, - ): - """ - .. deprecated:: 9.2.0 - - Use :py:meth:`getlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. - - See :ref:`deprecations ` for more information. - - Returns width and height (in pixels) of given text if rendered in font with - provided direction, features, and language. - - .. note:: For historical reasons this function measures text height from - the ascender line instead of the top, see :ref:`text-anchors`. - If you wish to measure text height from the top, it is recommended - to use the bottom value of :meth:`getbbox` with ``anchor='lt'`` instead. - - :param text: Text to measure. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :return: (width, height) - """ - deprecate("getsize", 10, "getbbox or getlength") - # vertical offset is added for historical reasons - # see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929 - size, offset = self.font.getsize(text, "L", direction, features, language) - return ( - size[0] + stroke_width * 2, - size[1] + stroke_width * 2 + offset[1], - ) - - def getsize_multiline( - self, - text, - direction=None, - spacing=4, - features=None, - language=None, - stroke_width=0, - ): - """ - .. deprecated:: 9.2.0 - - Use :py:meth:`.ImageDraw.multiline_textbbox` instead. - - See :ref:`deprecations ` for more information. - - Returns width and height (in pixels) of given text if rendered in font - with provided direction, features, and language, while respecting - newline characters. - - :param text: Text to measure. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - :param spacing: The vertical gap between lines, defaulting to 4 pixels. - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :return: (width, height) - """ - deprecate("getsize_multiline", 10, "ImageDraw.multiline_textbbox") - max_width = 0 - lines = self._multiline_split(text) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing - for line in lines: - line_width, line_height = self.getsize( - line, direction, features, language, stroke_width - ) - max_width = max(max_width, line_width) - - return max_width, len(lines) * line_spacing - spacing - - def getoffset(self, text): - """ - .. deprecated:: 9.2.0 - - Use :py:meth:`.getbbox` instead. - - See :ref:`deprecations ` for more information. - - Returns the offset of given text. This is the gap between the - starting coordinate and the first marking. Note that this gap is - included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`. - - :param text: Text to measure. - - :return: A tuple of the x and y offset - """ - deprecate("getoffset", 10, "getbbox") - return self.font.getsize(text)[1] - def getmask( self, text, @@ -851,22 +674,6 @@ def __init__(self, font, orientation=None): self.font = font self.orientation = orientation # any 'transpose' argument, or None - def getsize(self, text, *args, **kwargs): - """ - .. deprecated:: 9.2.0 - - Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. - - See :ref:`deprecations ` for more information. - """ - deprecate("getsize", 10, "getbbox or getlength") - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w, h = self.font.getsize(text) - if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - return h, w - return w, h - def getmask(self, text, mode="", *args, **kwargs): im = self.font.getmask(text, mode, *args, **kwargs) if self.orientation is not None: diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py index 81f2189dcfc..2f2a3df13e3 100644 --- a/src/PIL/_deprecate.py +++ b/src/PIL/_deprecate.py @@ -45,8 +45,6 @@ def deprecate( elif when <= int(__version__.split(".")[0]): msg = f"{deprecated} {is_} deprecated and should be removed." raise RuntimeError(msg) - elif when == 10: - removed = "Pillow 10 (2023-07-01)" elif when == 11: removed = "Pillow 11 (2024-10-15)" else: From adbb04d5dca5ae4f59ce51ca7474a3918e1f377d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 5 Apr 2023 12:32:13 +0300 Subject: [PATCH 2/6] Formatting for readability --- Tests/test_imagefont.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index f43044efe8f..a614b0fe59f 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -306,7 +306,10 @@ def test_rotated_transposed_font(font, orientation): bbox_b = draw.textbbox((20, 20), word) # Check (w,h) of box a is (h,w) of box b - assert (bbox_a[2] - bbox_a[0], bbox_a[3] - bbox_a[1]) == ( + assert ( + bbox_a[2] - bbox_a[0], + bbox_a[3] - bbox_a[1], + ) == ( bbox_b[3] - bbox_b[1], bbox_b[2] - bbox_b[0], ) @@ -349,7 +352,10 @@ def test_unrotated_transposed_font(font, orientation): length_b = draw.textlength(word) # Check boxes a and b are same size - assert (bbox_a[2] - bbox_a[0], bbox_a[3] - bbox_a[1]) == ( + assert ( + bbox_a[2] - bbox_a[0], + bbox_a[3] - bbox_a[1], + ) == ( bbox_b[2] - bbox_b[0], bbox_b[3] - bbox_b[1], ) From b27794fc01c84b876fe876e8ebbd8b4b6a4f78ce Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Apr 2023 17:24:16 +1000 Subject: [PATCH 3/6] Added test for ImageDraw2 textbbox --- Tests/test_imagedraw2.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 2a5219893ac..a8a2ee1fce6 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -2,7 +2,7 @@ import pytest -from PIL import Image, ImageDraw, ImageDraw2 +from PIL import Image, ImageDraw, ImageDraw2, features from .helper import ( assert_image_equal, @@ -170,6 +170,21 @@ def test_text(): assert_image_similar_tofile(im, expected, 13) +@skip_unless_feature("freetype2") +def test_textbbox(): + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw2.Draw(im) + font = ImageDraw2.Font("white", FONT_PATH) + + # Act + bbox = draw.textbbox((0, 0), "ImageDraw2", font) + + # Assert + right = 72 if features.check_feature("raqm") else 70 + assert bbox == (0, 2, right, 12) + + @skip_unless_feature("freetype2") def test_textsize_empty_string(): # Arrange From fa6cd4a19519c7c6af06265e39dc5f0e51fdd734 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Apr 2023 17:34:33 +1000 Subject: [PATCH 4/6] Only check width and height of transposed fonts once --- Tests/test_imagefont.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index a614b0fe59f..623365d53fe 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -305,7 +305,7 @@ def test_rotated_transposed_font(font, orientation): draw.font = transposed_font bbox_b = draw.textbbox((20, 20), word) - # Check (w,h) of box a is (h,w) of box b + # Check (w, h) of box a is (h, w) of box b assert ( bbox_a[2] - bbox_a[0], bbox_a[3] - bbox_a[1], @@ -314,11 +314,8 @@ def test_rotated_transposed_font(font, orientation): bbox_b[2] - bbox_b[0], ) - # Check bbox b is (20, 20, 20 + h, 20 + w) - assert bbox_b[0] == 20 - assert bbox_b[1] == 20 - assert bbox_b[2] == 20 + bbox_a[3] - bbox_a[1] - assert bbox_b[3] == 20 + bbox_a[2] - bbox_a[0] + # Check top left co-ordinates are correct + assert bbox_b[:2] == (20, 20) # text length is undefined for vertical text with pytest.raises(ValueError): @@ -360,11 +357,8 @@ def test_unrotated_transposed_font(font, orientation): bbox_b[3] - bbox_b[1], ) - # Check bbox b is (20, 20, 20 + w, 20 + h) - assert bbox_b[0] == 20 - assert bbox_b[1] == 20 - assert bbox_b[2] == 20 + bbox_a[2] - bbox_a[0] - assert bbox_b[3] == 20 + bbox_a[3] - bbox_a[1] + # Check top left co-ordinates are correct + assert bbox_b[:2] == (20, 20) assert length_a == length_b From bc0bf5efea357a18c8f10a0c5768e837891b808e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Apr 2023 18:47:58 +1000 Subject: [PATCH 5/6] Preserve line spacing backwards compatibility --- Tests/images/imagedraw_stroke_multiline.png | Bin 4059 -> 4061 bytes Tests/test_imagedraw.py | 4 ++-- src/PIL/ImageDraw.py | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Tests/images/imagedraw_stroke_multiline.png b/Tests/images/imagedraw_stroke_multiline.png index c290fc0568e5c4b40299a208cde7d0d760077fd0..fc5e07c8679d5aa12a1d95152ea6d2542f169697 100644 GIT binary patch delta 3990 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 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 17ee75dfa5f..7ffd7969d90 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1220,8 +1220,8 @@ def test_textbbox_stroke(): # Act / Assert assert draw.textbbox((2, 2), "A", font, stroke_width=2) == (0, 4, 16, 20) assert draw.textbbox((2, 2), "A", font, stroke_width=4) == (-2, 2, 18, 22) - assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=2) == (0, 4, 52, 42) - assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 46) + assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=2) == (0, 4, 52, 44) + assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 50) @skip_unless_feature("freetype2") diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index c16ff28cc8e..e9ccf80413f 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -431,7 +431,11 @@ def _multiline_split(self, text): return text.split(split_character) def _multiline_spacing(self, font, spacing, stroke_width): - return self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3] + spacing + return ( + self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3] + + stroke_width + + spacing + ) def text( self, From 16aa710c7804c24b8518d50927de92aac7cf5b1e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Apr 2023 20:14:15 +1000 Subject: [PATCH 6/6] Updated documentation --- docs/deprecations.rst | 2 - docs/reference/ImageDraw.rst | 110 ----------------------------------- 2 files changed, 112 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 64d0569c86b..45b2f42000f 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,8 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -.. _Font size and offset methods: - PSFile ~~~~~~ diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 43a5a2bc2b3..aec7a3ef89f 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -474,116 +474,6 @@ Methods .. versionadded:: 8.0.0 -.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) - - .. deprecated:: 9.2.0 - - See :ref:`deprecations ` for more information. - - Use :py:meth:`textlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. - - Return the size of the given string, in pixels. - - .. note:: For historical reasons this function measures text height from - the ascender line instead of the top, see :ref:`text-anchors`. - If you wish to measure text height from the top, it is recommended - to use :meth:`textbbox` with ``anchor='lt'`` instead. - - :param text: Text to be measured. If it contains any newline characters, - the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`. - :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: If the text is passed on to - :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`, - the number of pixels between lines. - :param direction: Direction of the text. It can be ``"rtl"`` (right to - left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example ``"dlig"`` or ``"ss01"``, but can be also - used to turn off default font features, for - example ``"-liga"`` to disable ligatures or ``"-kern"`` - to disable kerning. To get all supported - features, see `OpenType docs`_. - Requires libraqm. - - .. versionadded:: 4.2.0 - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code`_. - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :return: (width, height) - -.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) - - .. deprecated:: 9.2.0 - - See :ref:`deprecations ` for more information. - - Use :py:meth:`.multiline_textbbox` instead. - - Return the size of the given string, in pixels. - - Use :py:meth:`textlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. - - .. note:: For historical reasons this function measures text height as the - distance between the top ascender line and bottom descender line, - not the top and bottom of the text, see :ref:`text-anchors`. - If you wish to measure text height from the top to the bottom of text, - it is recommended to use :meth:`multiline_textbbox` instead. - - :param text: Text to be measured. - :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: The number of pixels between lines. - :param direction: Direction of the text. It can be ``"rtl"`` (right to - left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example ``"dlig"`` or ``"ss01"``, but can be also - used to turn off default font features, for - example ``"-liga"`` to disable ligatures or ``"-kern"`` - to disable kerning. To get all supported - features, see `OpenType docs`_. - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code`_. - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :return: (width, height) - .. py:method:: ImageDraw.textlength(text, font=None, direction=None, features=None, language=None, embedded_color=False) Returns length (in pixels with 1/64 precision) of given text when rendered