Testing complex text layout.
|
@ -203,12 +203,10 @@ class ImageDraw(object):
|
||||||
return text.split(split_character)
|
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,
|
||||||
*args, **kwargs, direction=None, features=None):
|
direction=None, features=None, *args, **kwargs):
|
||||||
|
|
||||||
if self._multiline_check(text):
|
if self._multiline_check(text):
|
||||||
return self.multiline_text(xy, text, fill, font, anchor,
|
return self.multiline_text(xy, text, fill, font, anchor,
|
||||||
*args, **kwargs, direction=direction, features=features)
|
direction=direction, features=features, *args, **kwargs)
|
||||||
|
|
||||||
ink, fill = self._getink(fill)
|
ink, fill = self._getink(fill)
|
||||||
if font is None:
|
if font is None:
|
||||||
font = self.getfont()
|
font = self.getfont()
|
||||||
|
|
|
@ -5,6 +5,7 @@ modules = {
|
||||||
"tkinter": "PIL._tkinter_finder",
|
"tkinter": "PIL._tkinter_finder",
|
||||||
"freetype2": "PIL._imagingft",
|
"freetype2": "PIL._imagingft",
|
||||||
"littlecms2": "PIL._imagingcms",
|
"littlecms2": "PIL._imagingcms",
|
||||||
|
"raqm": "PIL._imagingft",
|
||||||
"webp": "PIL._webp",
|
"webp": "PIL._webp",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
Tests/fonts/NotoNastaliqUrdu-Regular.ttf
Normal file
BIN
Tests/images/test_Nastalifont_text.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
Tests/images/test_arabictext_features.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/test_complex_unicode_text.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
Tests/images/test_direction_ltr.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Tests/images/test_direction_rtl.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Tests/images/test_kerning_features.png
Normal file
After Width: | Height: | Size: 963 B |
BIN
Tests/images/test_ligature_features.png
Normal file
After Width: | Height: | Size: 605 B |
BIN
Tests/images/test_text.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Tests/images/test_y_offset.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
136
Tests/test_imagefontctl.py
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from helper import unittest, PillowTestCase
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageDraw
|
||||||
|
|
||||||
|
FONT_SIZE = 20
|
||||||
|
FONT_PATH = "Tests/fonts/DejaVuSans.ttf"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import ImageFont
|
||||||
|
|
||||||
|
# check if raqm is available
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'TEST', font=ttf, fill=500, direction='ltr')
|
||||||
|
|
||||||
|
class TestImagecomplextext(PillowTestCase):
|
||||||
|
|
||||||
|
def test_complex_text(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'اهلا عمان', font=ttf, fill=500)
|
||||||
|
|
||||||
|
target = 'Tests/images/test_text.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_y_offset(self):
|
||||||
|
ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'هنا عمان', font=ttf, fill=500)
|
||||||
|
|
||||||
|
target = 'Tests/images/test_y_offset.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_complex_unicode_text(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), u'مرحبا بكم', font=ttf, fill=500)
|
||||||
|
|
||||||
|
target = 'Tests/images/test_complex_unicode_text.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_text_direction_rtl(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'English عربي', font=ttf, fill=500, direction='rtl')
|
||||||
|
|
||||||
|
target = 'Tests/images/test_direction_rtl.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_text_direction_ltr(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'سلطنة عمان Oman', font=ttf, fill=500, direction='ltr')
|
||||||
|
|
||||||
|
target = 'Tests/images/test_direction_ltr.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_text_direction_rtl2(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'Oman سلطنة عمان', font=ttf, fill=500, direction='rtl')
|
||||||
|
|
||||||
|
target = 'Tests/images/test_direction_ltr.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_ligature_features(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'filling', font=ttf, fill=500, features=['-liga'])
|
||||||
|
|
||||||
|
target = 'Tests/images/test_ligature_features.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_kerning_features(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'TeToAV', font=ttf, fill=500, features=['-kern'])
|
||||||
|
|
||||||
|
target = 'Tests/images/test_kerning_features.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
def test_arabictext_features(self):
|
||||||
|
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||||
|
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, features=['-fina','-init','-medi'])
|
||||||
|
|
||||||
|
target = 'Tests/images/test_arabictext_features.png'
|
||||||
|
target_img = Image.open(target)
|
||||||
|
|
||||||
|
self.assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
except (KeyError, ImportError):
|
||||||
|
class TestImagecomplextext(PillowTestCase):
|
||||||
|
def test_skip(self):
|
||||||
|
self.skipTest("KeyError")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
|
||||||
|
# End of file
|
10
_imagingft.c
|
@ -588,17 +588,21 @@ font_render(FontObject* self, PyObject* args)
|
||||||
|
|
||||||
for (x = i = 0; i < count; i++) {
|
for (x = i = 0; i < count; i++) {
|
||||||
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0)
|
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0)
|
||||||
x = -PIXEL(self->face->glyph->metrics.horiBearingX);
|
x = -self->face->glyph->metrics.horiBearingX;
|
||||||
index = glyph_info[i].index;
|
index = glyph_info[i].index;
|
||||||
|
|
||||||
error = FT_Load_Glyph(self->face, index, load_flags);
|
error = FT_Load_Glyph(self->face, index, load_flags);
|
||||||
if (error)
|
if (error)
|
||||||
return geterror(error);
|
return geterror(error);
|
||||||
|
|
||||||
|
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) {
|
||||||
|
x = -self->face->glyph->metrics.horiBearingX;
|
||||||
|
}
|
||||||
|
|
||||||
glyph = self->face->glyph;
|
glyph = self->face->glyph;
|
||||||
|
|
||||||
source = (unsigned char*) glyph->bitmap.buffer;
|
source = (unsigned char*) glyph->bitmap.buffer;
|
||||||
xx = x + glyph->bitmap_left;
|
xx = PIXEL(x) + glyph->bitmap_left;
|
||||||
xx += PIXEL(glyph_info[i].x_offset);
|
xx += PIXEL(glyph_info[i].x_offset);
|
||||||
x0 = 0;
|
x0 = 0;
|
||||||
x1 = glyph->bitmap.width;
|
x1 = glyph->bitmap.width;
|
||||||
|
@ -644,7 +648,7 @@ font_render(FontObject* self, PyObject* args)
|
||||||
source += glyph->bitmap.pitch;
|
source += glyph->bitmap.pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x += PIXEL(glyph_info[i].x_advance);
|
x += glyph_info[i].x_advance;
|
||||||
}
|
}
|
||||||
PyMem_Del(glyph_info);
|
PyMem_Del(glyph_info);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|
|
@ -183,7 +183,8 @@ if __name__ == "__main__":
|
||||||
("jpg", "JPEG"),
|
("jpg", "JPEG"),
|
||||||
("jpg_2000", "OPENJPEG (JPEG2000)"),
|
("jpg_2000", "OPENJPEG (JPEG2000)"),
|
||||||
("zlib", "ZLIB (PNG/ZIP)"),
|
("zlib", "ZLIB (PNG/ZIP)"),
|
||||||
("libtiff", "LIBTIFF")
|
("libtiff", "LIBTIFF"),
|
||||||
|
("raqm", "RAQM")
|
||||||
]:
|
]:
|
||||||
if features.check(name):
|
if features.check(name):
|
||||||
print("---", feature, "support ok")
|
print("---", feature, "support ok")
|
||||||
|
|