mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Added text stroking
This commit is contained in:
parent
f3f45cfec5
commit
f93a5d0972
BIN
Tests/images/imagedraw_stroke_different.png
Normal file
BIN
Tests/images/imagedraw_stroke_different.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
Tests/images/imagedraw_stroke_multiline.png
Normal file
BIN
Tests/images/imagedraw_stroke_multiline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
BIN
Tests/images/imagedraw_stroke_same.png
Normal file
BIN
Tests/images/imagedraw_stroke_same.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
Tests/images/test_direction_ttb_stroke.png
Normal file
BIN
Tests/images/test_direction_ttb_stroke.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
|
@ -1,8 +1,8 @@
|
|||
import os.path
|
||||
|
||||
from PIL import Image, ImageColor, ImageDraw
|
||||
from PIL import Image, ImageColor, ImageDraw, ImageFont, features
|
||||
|
||||
from .helper import PillowTestCase, hopper
|
||||
from .helper import PillowTestCase, hopper, unittest
|
||||
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
|
@ -29,6 +29,8 @@ POINTS2 = [10, 10, 20, 40, 30, 30]
|
|||
|
||||
KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)]
|
||||
|
||||
HAS_FREETYPE = features.check("freetype2")
|
||||
|
||||
|
||||
class TestImageDraw(PillowTestCase):
|
||||
def test_sanity(self):
|
||||
|
@ -771,6 +773,54 @@ class TestImageDraw(PillowTestCase):
|
|||
draw.textsize("\n")
|
||||
draw.textsize("test\n")
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_textsize_stroke(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20)
|
||||
|
||||
# Act / Assert
|
||||
self.assertEqual(draw.textsize("A", font, stroke_width=2), (16, 20))
|
||||
self.assertEqual(
|
||||
draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2), (52, 44)
|
||||
)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_stroke(self):
|
||||
for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (120, 130))
|
||||
draw = ImageDraw.Draw(im)
|
||||
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
|
||||
|
||||
# Act
|
||||
draw.text(
|
||||
(10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill
|
||||
)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 2.8
|
||||
)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_stroke_multiline(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (100, 250))
|
||||
draw = ImageDraw.Draw(im)
|
||||
font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
|
||||
|
||||
# Act
|
||||
draw.multiline_text(
|
||||
(10, 10), "A\nB", "#f00", font, stroke_width=2, stroke_fill="#0f0"
|
||||
)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3
|
||||
)
|
||||
|
||||
def test_same_color_outline(self):
|
||||
# Prepare shape
|
||||
x0, y0 = 5, 5
|
||||
|
|
|
@ -605,6 +605,21 @@ class TestImageFont(PillowTestCase):
|
|||
self.assertEqual(t.getsize_multiline("ABC\nA"), (36, 36))
|
||||
self.assertEqual(t.getsize_multiline("ABC\nAaaa"), (48, 36))
|
||||
|
||||
def test_getsize_stroke(self):
|
||||
# Arrange
|
||||
t = self.get_font()
|
||||
|
||||
# Act / Assert
|
||||
for stroke_width in [0, 2]:
|
||||
self.assertEqual(
|
||||
t.getsize("A", stroke_width=stroke_width),
|
||||
(12 + stroke_width * 2, 16 + stroke_width * 2),
|
||||
)
|
||||
self.assertEqual(
|
||||
t.getsize_multiline("ABC\nAaaa", stroke_width=stroke_width),
|
||||
(48 + stroke_width * 2, 36 + stroke_width * 4),
|
||||
)
|
||||
|
||||
def test_complex_font_settings(self):
|
||||
# Arrange
|
||||
t = self.get_font()
|
||||
|
|
|
@ -115,6 +115,30 @@ class TestImagecomplextext(PillowTestCase):
|
|||
|
||||
self.assert_image_similar(im, target_img, 1.15)
|
||||
|
||||
def test_text_direction_ttb_stroke(self):
|
||||
ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", 50)
|
||||
|
||||
im = Image.new(mode="RGB", size=(100, 300))
|
||||
draw = ImageDraw.Draw(im)
|
||||
try:
|
||||
draw.text(
|
||||
(25, 25),
|
||||
"あい",
|
||||
font=ttf,
|
||||
fill=500,
|
||||
direction="ttb",
|
||||
stroke_width=2,
|
||||
stroke_fill="#0f0",
|
||||
)
|
||||
except ValueError as ex:
|
||||
if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction":
|
||||
self.skipTest("libraqm 0.7 or greater not available")
|
||||
|
||||
target = "Tests/images/test_direction_ttb_stroke.png"
|
||||
target_img = Image.open(target)
|
||||
|
||||
self.assert_image_similar(im, target_img, 12.4)
|
||||
|
||||
def test_ligature_features(self):
|
||||
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ Methods
|
|||
|
||||
Draw a shape.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None, language=None)
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None)
|
||||
|
||||
Draws the string at the given position.
|
||||
|
||||
|
@ -297,6 +297,15 @@ Methods
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
:param stroke_fill: Color to use for the text stroke. If not given, will default to
|
||||
the ``fill`` parameter.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None, language=None)
|
||||
|
||||
Draws the string at the given position.
|
||||
|
@ -336,7 +345,7 @@ Methods
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None)
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
|
@ -372,7 +381,11 @@ Methods
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None)
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0)
|
||||
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
|
@ -408,6 +421,10 @@ Methods
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None)
|
||||
|
||||
.. warning:: This method is experimental.
|
||||
|
|
|
@ -261,24 +261,95 @@ class ImageDraw(object):
|
|||
|
||||
return text.split(split_character)
|
||||
|
||||
def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs):
|
||||
def text(
|
||||
self,
|
||||
xy,
|
||||
text,
|
||||
fill=None,
|
||||
font=None,
|
||||
anchor=None,
|
||||
spacing=4,
|
||||
align="left",
|
||||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
stroke_fill=None,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
if self._multiline_check(text):
|
||||
return self.multiline_text(xy, text, fill, font, anchor, *args, **kwargs)
|
||||
ink, fill = self._getink(fill)
|
||||
return self.multiline_text(
|
||||
xy,
|
||||
text,
|
||||
fill,
|
||||
font,
|
||||
anchor,
|
||||
spacing,
|
||||
align,
|
||||
direction,
|
||||
features,
|
||||
language,
|
||||
stroke_width,
|
||||
stroke_fill,
|
||||
)
|
||||
|
||||
if font is None:
|
||||
font = self.getfont()
|
||||
|
||||
def getink(fill):
|
||||
ink, fill = self._getink(fill)
|
||||
if ink is None:
|
||||
ink = fill
|
||||
if ink is not None:
|
||||
return fill
|
||||
return ink
|
||||
|
||||
def drawText(ink, stroke_width=0, stroke_offset=None):
|
||||
coord = xy
|
||||
try:
|
||||
mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs)
|
||||
xy = xy[0] + offset[0], xy[1] + offset[1]
|
||||
mask, offset = font.getmask2(
|
||||
text,
|
||||
self.fontmode,
|
||||
direction=direction,
|
||||
features=features,
|
||||
language=language,
|
||||
stroke_width=stroke_width,
|
||||
*args,
|
||||
**kwargs
|
||||
)
|
||||
coord = coord[0] + offset[0], coord[1] + offset[1]
|
||||
except AttributeError:
|
||||
try:
|
||||
mask = font.getmask(text, self.fontmode, *args, **kwargs)
|
||||
mask = font.getmask(
|
||||
text,
|
||||
self.fontmode,
|
||||
direction,
|
||||
features,
|
||||
language,
|
||||
stroke_width,
|
||||
*args,
|
||||
**kwargs
|
||||
)
|
||||
except TypeError:
|
||||
mask = font.getmask(text)
|
||||
self.draw.draw_bitmap(xy, mask, ink)
|
||||
if stroke_offset:
|
||||
coord = coord[0] + stroke_offset[0], coord[1] + stroke_offset[1]
|
||||
self.draw.draw_bitmap(coord, mask, ink)
|
||||
|
||||
ink = getink(fill)
|
||||
if ink is not None:
|
||||
stroke_ink = None
|
||||
if stroke_width:
|
||||
stroke_ink = getink(stroke_fill) if stroke_fill is not None else ink
|
||||
|
||||
if stroke_ink is not None:
|
||||
# Draw stroked text
|
||||
drawText(stroke_ink, stroke_width)
|
||||
|
||||
# Draw normal text
|
||||
drawText(ink, 0, (stroke_width, stroke_width))
|
||||
else:
|
||||
# Only draw normal text
|
||||
drawText(ink)
|
||||
|
||||
def multiline_text(
|
||||
self,
|
||||
|
@ -292,14 +363,23 @@ class ImageDraw(object):
|
|||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
stroke_fill=None,
|
||||
):
|
||||
widths = []
|
||||
max_width = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self.textsize("A", font=font)[1] + spacing
|
||||
line_spacing = (
|
||||
self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing
|
||||
)
|
||||
for line in lines:
|
||||
line_width, line_height = self.textsize(
|
||||
line, font, direction=direction, features=features, language=language
|
||||
line,
|
||||
font,
|
||||
direction=direction,
|
||||
features=features,
|
||||
language=language,
|
||||
stroke_width=stroke_width,
|
||||
)
|
||||
widths.append(line_width)
|
||||
max_width = max(max_width, line_width)
|
||||
|
@ -322,32 +402,50 @@ class ImageDraw(object):
|
|||
direction=direction,
|
||||
features=features,
|
||||
language=language,
|
||||
stroke_width=stroke_width,
|
||||
stroke_fill=stroke_fill,
|
||||
)
|
||||
top += line_spacing
|
||||
left = xy[0]
|
||||
|
||||
def textsize(
|
||||
self, text, font=None, spacing=4, direction=None, features=None, language=None
|
||||
self,
|
||||
text,
|
||||
font=None,
|
||||
spacing=4,
|
||||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
):
|
||||
"""Get the size of a given string, in pixels."""
|
||||
if self._multiline_check(text):
|
||||
return self.multiline_textsize(
|
||||
text, font, spacing, direction, features, language
|
||||
text, font, spacing, direction, features, language, stroke_width
|
||||
)
|
||||
|
||||
if font is None:
|
||||
font = self.getfont()
|
||||
return font.getsize(text, direction, features, language)
|
||||
return font.getsize(text, direction, features, language, stroke_width)
|
||||
|
||||
def multiline_textsize(
|
||||
self, text, font=None, spacing=4, direction=None, features=None, language=None
|
||||
self,
|
||||
text,
|
||||
font=None,
|
||||
spacing=4,
|
||||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
):
|
||||
max_width = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self.textsize("A", font=font)[1] + spacing
|
||||
line_spacing = (
|
||||
self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing
|
||||
)
|
||||
for line in lines:
|
||||
line_width, line_height = self.textsize(
|
||||
line, font, spacing, direction, features, language
|
||||
line, font, spacing, direction, features, language, stroke_width
|
||||
)
|
||||
max_width = max(max_width, line_width)
|
||||
return max_width, len(lines) * line_spacing - spacing
|
||||
|
|
|
@ -207,7 +207,9 @@ class FreeTypeFont(object):
|
|||
"""
|
||||
return self.font.ascent, self.font.descent
|
||||
|
||||
def getsize(self, text, direction=None, features=None, language=None):
|
||||
def getsize(
|
||||
self, text, direction=None, features=None, language=None, stroke_width=0
|
||||
):
|
||||
"""
|
||||
Returns width and height (in pixels) of given text if rendered in font with
|
||||
provided direction, features, and language.
|
||||
|
@ -243,13 +245,26 @@ class FreeTypeFont(object):
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
:return: (width, height)
|
||||
"""
|
||||
size, offset = self.font.getsize(text, direction, features, language)
|
||||
return (size[0] + offset[0], size[1] + offset[1])
|
||||
return (
|
||||
size[0] + stroke_width * 2 + offset[0],
|
||||
size[1] + stroke_width * 2 + offset[1],
|
||||
)
|
||||
|
||||
def getsize_multiline(
|
||||
self, text, direction=None, spacing=4, features=None, language=None
|
||||
self,
|
||||
text,
|
||||
direction=None,
|
||||
spacing=4,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
):
|
||||
"""
|
||||
Returns width and height (in pixels) of given text if rendered in font
|
||||
|
@ -285,13 +300,19 @@ class FreeTypeFont(object):
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
:return: (width, height)
|
||||
"""
|
||||
max_width = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self.getsize("A")[1] + spacing
|
||||
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)
|
||||
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
|
||||
|
@ -308,7 +329,15 @@ class FreeTypeFont(object):
|
|||
"""
|
||||
return self.font.getsize(text)[1]
|
||||
|
||||
def getmask(self, text, mode="", direction=None, features=None, language=None):
|
||||
def getmask(
|
||||
self,
|
||||
text,
|
||||
mode="",
|
||||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
):
|
||||
"""
|
||||
Create a bitmap for the text.
|
||||
|
||||
|
@ -352,11 +381,20 @@ class FreeTypeFont(object):
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.0
|
||||
|
||||
:return: An internal PIL storage memory instance as defined by the
|
||||
:py:mod:`PIL.Image.core` interface module.
|
||||
"""
|
||||
return self.getmask2(
|
||||
text, mode, direction=direction, features=features, language=language
|
||||
text,
|
||||
mode,
|
||||
direction=direction,
|
||||
features=features,
|
||||
language=language,
|
||||
stroke_width=stroke_width,
|
||||
)[0]
|
||||
|
||||
def getmask2(
|
||||
|
@ -367,6 +405,7 @@ class FreeTypeFont(object):
|
|||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
stroke_width=0,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
|
@ -413,13 +452,20 @@ class FreeTypeFont(object):
|
|||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param stroke_width: The width of the text stroke.
|
||||
|
||||
.. versionadded:: 6.2.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
|
||||
"""
|
||||
size, offset = self.font.getsize(text, direction, features, language)
|
||||
size = size[0] + stroke_width * 2, size[1] + stroke_width * 2
|
||||
im = fill("L", size, 0)
|
||||
self.font.render(text, im.id, mode == "1", direction, features, language)
|
||||
self.font.render(
|
||||
text, im.id, mode == "1", direction, features, language, stroke_width
|
||||
)
|
||||
return im, offset
|
||||
|
||||
def font_variant(
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
#include FT_STROKER_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#include FT_SFNT_NAMES_H
|
||||
|
||||
|
@ -790,7 +791,13 @@ font_render(FontObject* self, PyObject* args)
|
|||
int index, error, ascender, horizontal_dir;
|
||||
int load_flags;
|
||||
unsigned char *source;
|
||||
FT_GlyphSlot glyph;
|
||||
FT_Glyph glyph;
|
||||
FT_GlyphSlot glyph_slot;
|
||||
FT_Bitmap bitmap;
|
||||
FT_BitmapGlyph bitmap_glyph;
|
||||
int stroke_width = 0;
|
||||
FT_Stroker stroker = NULL;
|
||||
FT_Int left;
|
||||
/* render string into given buffer (the buffer *must* have
|
||||
the right size, or this will crash) */
|
||||
PyObject* string;
|
||||
|
@ -806,7 +813,8 @@ font_render(FontObject* self, PyObject* args)
|
|||
GlyphInfo *glyph_info;
|
||||
PyObject *features = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "On|izOz:render", &string, &id, &mask, &dir, &features, &lang)) {
|
||||
if (!PyArg_ParseTuple(args, "On|izOzi:render", &string, &id, &mask, &dir, &features, &lang,
|
||||
&stroke_width)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -819,21 +827,37 @@ font_render(FontObject* self, PyObject* args)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
if (stroke_width) {
|
||||
error = FT_Stroker_New(library, &stroker);
|
||||
if (error) {
|
||||
return geterror(error);
|
||||
}
|
||||
|
||||
FT_Stroker_Set(stroker, (FT_Fixed)stroke_width*64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
|
||||
}
|
||||
|
||||
im = (Imaging) id;
|
||||
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
|
||||
load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP;
|
||||
if (mask)
|
||||
load_flags = FT_LOAD_NO_BITMAP;
|
||||
if (stroker == NULL) {
|
||||
load_flags |= FT_LOAD_RENDER;
|
||||
}
|
||||
if (mask) {
|
||||
load_flags |= FT_LOAD_TARGET_MONO;
|
||||
}
|
||||
|
||||
ascender = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
index = glyph_info[i].index;
|
||||
error = FT_Load_Glyph(self->face, index, load_flags);
|
||||
if (error)
|
||||
if (error) {
|
||||
return geterror(error);
|
||||
}
|
||||
|
||||
glyph = self->face->glyph;
|
||||
temp = glyph->bitmap.rows - glyph->bitmap_top;
|
||||
glyph_slot = self->face->glyph;
|
||||
bitmap = glyph_slot->bitmap;
|
||||
|
||||
temp = bitmap.rows - glyph_slot->bitmap_top;
|
||||
temp -= PIXEL(glyph_info[i].y_offset);
|
||||
if (temp > ascender)
|
||||
ascender = temp;
|
||||
|
@ -844,37 +868,62 @@ font_render(FontObject* self, PyObject* args)
|
|||
for (i = 0; i < count; i++) {
|
||||
index = glyph_info[i].index;
|
||||
error = FT_Load_Glyph(self->face, index, load_flags);
|
||||
if (error)
|
||||
if (error) {
|
||||
return geterror(error);
|
||||
}
|
||||
|
||||
glyph = self->face->glyph;
|
||||
if (horizontal_dir) {
|
||||
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) {
|
||||
x = -self->face->glyph->metrics.horiBearingX;
|
||||
glyph_slot = self->face->glyph;
|
||||
if (stroker != NULL) {
|
||||
error = FT_Get_Glyph(glyph_slot, &glyph);
|
||||
if (!error) {
|
||||
error = FT_Glyph_Stroke(&glyph, stroker, 1);
|
||||
}
|
||||
xx = PIXEL(x) + glyph->bitmap_left;
|
||||
xx += PIXEL(glyph_info[i].x_offset);
|
||||
if (!error) {
|
||||
FT_Vector origin = {0, 0};
|
||||
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, &origin, 1);
|
||||
}
|
||||
if (error) {
|
||||
return geterror(error);
|
||||
}
|
||||
|
||||
bitmap_glyph = (FT_BitmapGlyph)glyph;
|
||||
|
||||
bitmap = bitmap_glyph->bitmap;
|
||||
left = bitmap_glyph->left;
|
||||
|
||||
FT_Done_Glyph(glyph);
|
||||
} else {
|
||||
if (self->face->glyph->metrics.vertBearingX < 0) {
|
||||
x = -self->face->glyph->metrics.vertBearingX;
|
||||
bitmap = glyph_slot->bitmap;
|
||||
left = glyph_slot->bitmap_left;
|
||||
}
|
||||
xx = im->xsize / 2 - glyph->bitmap.width / 2;
|
||||
|
||||
if (horizontal_dir) {
|
||||
if (i == 0 && glyph_slot->metrics.horiBearingX < 0) {
|
||||
x = -glyph_slot->metrics.horiBearingX;
|
||||
}
|
||||
xx = PIXEL(x) + left;
|
||||
xx += PIXEL(glyph_info[i].x_offset) + stroke_width;
|
||||
} else {
|
||||
if (glyph_slot->metrics.vertBearingX < 0) {
|
||||
x = -glyph_slot->metrics.vertBearingX;
|
||||
}
|
||||
xx = im->xsize / 2 - bitmap.width / 2;
|
||||
}
|
||||
|
||||
x0 = 0;
|
||||
x1 = glyph->bitmap.width;
|
||||
x1 = bitmap.width;
|
||||
if (xx < 0)
|
||||
x0 = -xx;
|
||||
if (xx + x1 > im->xsize)
|
||||
x1 = im->xsize - xx;
|
||||
|
||||
source = (unsigned char*) glyph->bitmap.buffer;
|
||||
for (bitmap_y = 0; bitmap_y < glyph->bitmap.rows; bitmap_y++) {
|
||||
source = (unsigned char*) bitmap.buffer;
|
||||
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++) {
|
||||
if (horizontal_dir) {
|
||||
yy = bitmap_y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender);
|
||||
yy -= PIXEL(glyph_info[i].y_offset);
|
||||
yy = bitmap_y + im->ysize - (PIXEL(glyph_slot->metrics.horiBearingY) + ascender);
|
||||
yy -= PIXEL(glyph_info[i].y_offset) + stroke_width * 2;
|
||||
} else {
|
||||
yy = bitmap_y + PIXEL(y + glyph->metrics.vertBearingY) + ascender;
|
||||
yy = bitmap_y + PIXEL(y + glyph_slot->metrics.vertBearingY) + ascender;
|
||||
yy += PIXEL(glyph_info[i].y_offset);
|
||||
}
|
||||
if (yy >= 0 && yy < im->ysize) {
|
||||
|
@ -900,12 +949,13 @@ font_render(FontObject* self, PyObject* args)
|
|||
}
|
||||
}
|
||||
}
|
||||
source += glyph->bitmap.pitch;
|
||||
source += bitmap.pitch;
|
||||
}
|
||||
x += glyph_info[i].x_advance;
|
||||
y -= glyph_info[i].y_advance;
|
||||
}
|
||||
|
||||
FT_Stroker_Done(stroker);
|
||||
PyMem_Del(glyph_info);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user