Pillow不支持color emoji font!
我想在MAC下面用pillow把一些文本转换成PNG图片,在转普通文字的时候都没问题,但在遇到emoji字符的时候就搞不定了,代码如下:
import loggingimport PIL.Image import PIL.ImageDraw import PIL.ImageFont class Main(object):def TestEmoji(self): text = u'😄' fontName = '/System/Library/Fonts/Apple Color Emoji.ttf' pngPath = 'test.png' fontSize = 16 font = PIL.ImageFont.truetype('/System/Library/Fonts/Apple Color Emoji.ttf', 16, encoding='unic') width, height = font.getsize(text) logging.debug('(width, height) = (%d, %d)' % (width, height)) image = PIL.Image.new('RGBA', (width, height), (0, 0, 0, 0)) # 设置透明背景 draw = PIL.ImageDraw.Draw(image) draw.text((0, 0), text, font = font, fill = '#000000') image.save(pngPath) image.show()
得到如下错误:
File "test.py", line 37, in TestEmoji font = PIL.ImageFont.truetype('/System/Library/Fonts/Apple Color Emoji.ttf', 16, encoding='unic') File "/Users/palance/Downloads/Pillow-master/PIL/ImageFont.py", line 291, in truetype return FreeTypeFont(fontpath, size, index, encoding) File "/Users/palance/Downloads/Pillow-master/PIL/ImageFont.py", line 140, in __init__ self.font = core.getfont(font, size, index, encoding) IOError: invalid pixel size
根据提示,追到ImageFont.py:
…… try: from PIL import _imagingft as core except ImportError: core = _imagingft_not_installed() …… class FreeTypeFont(object): …… def __init__(self, font=None, size=10, index=0, encoding="", file=None): ……if isPath(font): self.font = core.getfont(font, size, index, encoding) # 出错的140行 else: ……
根据头部代码可以找到core是来自PIL/_imagingft,找到Pillow-master/PIL/_imagingft.so,这是由C写的so文件.再去追这段C代码,找到Pillow-master/_imagingft.c的getfont函数:
static PyObject* getfont(PyObject* self_, PyObject* args, PyObject* kw) { /* create a font object from a file name and a size (in pixels) */ FontObject* self; int error = 0; char* filename = NULL; int size; int index = 0; unsigned char* encoding; unsigned char* font_bytes; int font_bytes_size = 0; static char* kwlist[] = { "filename", "size", "index", "encoding", "font_bytes", NULL }; printf("1:%d\n", error); if (!library) { PyErr_SetString( PyExc_IOError, "failed to initialize FreeType library" ); return NULL; } printf("2:%d\n", error); if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist, Py_FileSystemDefaultEncoding, &filename, &size, &index, &encoding, &font_bytes, &font_bytes_size)) { return NULL; } printf("3:%d\n", error); self = PyObject_New(FontObject, &Font_Type); if (!self) { if (filename) PyMem_Free(filename); return NULL; } printf("4:%d\n", error); if (filename && font_bytes_size <= 0) { self->font_bytes = NULL; error = FT_New_Face(library, filename, index, &self->face); printf("4.1:%d\n, filename=%s, index=%d\n", error, filename, index); } else { /* need to have allocated storage for font_bytes for the life of the object.*/ /* Don't free this before FT_Done_Face */ self->font_bytes = PyMem_Malloc(font_bytes_size); if (!self->font_bytes) { error = 65; // Out of Memory in Freetype. } if (!error) { memcpy(self->font_bytes, font_bytes, (size_t)font_bytes_size); error = FT_New_Memory_Face(library, (FT_Byte*)self->font_bytes, font_bytes_size, index, &self->face); } printf("4.2:%d\n", error); } printf("5:%d\n", error); if (!error) error = FT_Set_Pixel_Sizes(self->face, 0, size); # 定位到问题出在这里 printf("6: error = %d, size = %d\n", error, size); if (!error && encoding && strlen((char*) encoding) == 4) { FT_Encoding encoding_tag = FT_MAKE_TAG( encoding[0], encoding[1], encoding[2], encoding[3] ); error = FT_Select_Charmap(self->face, encoding_tag); printf("6.1:%d\n", error); } if (filename) PyMem_Free(filename); printf("7:%d\n", error); if (error) { if (self->font_bytes) { PyMem_Free(self->font_bytes); } PyObject_Del(self); return geterror(error); } printf("8:%d\n", error); return (PyObject*) self; }
我在代码里面插入了一些调试信息,定位到问题发生在函数调用 FT_Set_Pixel_Sizes 里面,这是一个freetype函数的调用。难道是freetype不支持emoji字体?
继续追查freetype,我发现freetype是从2.5开始号称支持了color font,我用的是2.6,应该是没问题的。
我从https://gist.github.com/jokertarot/7583938找到一段支持color font的代码,仔细查看了使用freetype的前三步:
1、调用Init初始化库
2、构造FreeTypeFace对象,生成typeface
3、调用SetXXXFont,注意在这一步普通字体和Color字体是不一样的,普通字体调用了SetNormalFont,内部调用FT_Set_Pixel_Sizes;而Color字体调用SetColorFont,内部调用FT_Select_Size
再回过头来看Pillow-master/_imagingft.c的getfont函数,他是没有区分NormalFont和ColorFont的,一律按照NormalFont来处理了,所以出错。
这应该就是核心问题了:freetype支持color font,但在具体处理的时候需要使用不同的接口,在Pillow这一层没有考虑到这一点,因此是Pillow无法支持color font。要想解决问题,必须在Pillow-master/_imagingft.c的getfont里作出修改。
freetype那段支持color font的代码我还没试过,回头再专门研究一下。