使用Python将文本转为图片

2012-02-19

有时候,我们需要将文本转换为图片,比如发长微博,或者不想让人轻易复制我们的文本内容等时候。目前类似的工具已经有了不少,不过我觉得用得都不是很趁手,于是便自己尝试实现了一个。你可以先访问一下查看效果:txt2.im

在 Python 中,PIL (Python Imaging Library) 是最常用的绘图库,自然地,尝试从 PIL 开始。

1、使用 PIL 将文字转换为图片

说转换其实并不恰当,真实的过程是:先在内存中生成一张图片,将需要的文字绘制到这个图片上,再将图片保存到指定位置。代码如下:

# -*- coding: utf-8 -*-

import os
import Image, ImageFont, ImageDraw

text = u"这是一段测试文本,test 123。"

im = Image.new("RGB", (300, 50), (255, 255, 255))
dr = ImageDraw.Draw(im)
font = ImageFont.truetype(os.path.join("fonts", "msyh.ttf"), 14)

dr.text((10, 5), text, font=font, fill="#000000")

im.show()
im.save("t.png")

生成的图片如下:

杯具发生了,汉字没有正常显示!

网上搜索了一圈,发现这好像是 PIL 的一个 bug,PIL 目前的版本中,不能正确处理非 ASCII 字符的点阵字体的渲染。对于像宋体这样的字体来说,只有 >= 18px 时,才会被当作矢量字体处理,也就是说只有当字体 >= 18px 时,文字才能正常显示:

font = ImageFont.truetype(os.path.join("fonts", "simsun.ttc"), 18)

效果如下:

增大字体虽然解决了汉字不能正常显示的问题,但还是没有解决我们一开始的初衷:使用点阵字体进行渲染。但是,这个目标使用现阶段的 PIL 似乎有点难以实现了。

3、使用 pyGame 渲染点阵字体

Python 的第三方模块或组件非常多,可用来绘图的除了 PIL 之外,就还有 PycairomatplotlibpyGame 等。在这儿,我使用 pyGame 来完成点阵字体的渲染工作。

代码如下:

# -*- coding: utf-8 -*-

import os
import pygame

pygame.init()

text = u"这是一段测试文本,test 123。"
font = pygame.font.Font(os.path.join("fonts", "simsun.ttc"), 14)
rtext = font.render(text, True, (0, 0, 0), (255, 255, 255))

pygame.image.save(rtext, "t.jpg")

效果如下:

可以看到,使用 pyGame ,点阵字体的问题终于搞定了。

4、结合 PIL 和 pyGame

pyGame 虽然可以解决点阵字体的渲染问题,但讲到对图片的处理,还是 PIL 更为强大。那么,我们为什么不把两者结合起来呢?用 pyGame 渲染点阵字体,然后用 PIL 生成整张图片。

代码如下:

# -*- coding: utf-8 -*-

import os
import StringIO
import Image, ImageFont, ImageDraw
import pygame

pygame.init()

text = u"这是一段测试文本,test 123。"

im = Image.new("RGB", (300, 50), (255, 255, 255))
#dr = ImageDraw.Draw(im)
#font = ImageFont.truetype(os.path.join("fonts", "simsun.ttc"), 18)
font = pygame.font.Font(os.path.join("fonts", "simsun.ttc"), 14)

#dr.text((10, 5), text, font=font, fill="#000000")
rtext = font.render(text, True, (0, 0, 0), (255, 255, 255))

#pygame.image.save(rtext, "t.gif")
sio = StringIO.StringIO()
pygame.image.save(rtext, sio)
sio.seek(0)

line = Image.open(sio)
im.paste(line, (10, 5))

im.show()
im.save("t.png")

原理很简单,先将文字用 pyGame 渲染为图片,将渲染结果保存在一个 StringIO 对象中,然后再用 PIL 加载它。使用 StringIO 的好处是,一切操作都是在内存中进行的,不需要先将它保存到硬盘再用 PIL 读取,因为硬盘 IO 的效率相对来说是比较低的。

最终效果如下:

到这儿,使用 Python 将文本转为图片的功能就基本实现了,用到了 PIL 和 pyGame。

当然,上面的代码还只解决了最基本的问题,一个真正可用的文本转图片工具,还应该解决以下问题:长文本换行问题、英文单词断字问题、标点符号换行问题等。关于这些问题的分析篇幅也不短,这一次就先略过了。下面是一个综合考虑了诸多因素之后生成的《荷塘月色》的效果图:

【2017-10-25 更新】

文字转图片的核心代码见 https://gist.github.com/oldj/9c4d012d6fff059ccea7

分类: 编程 标签: Python
前一篇: 页面加载时间度量
后一篇: 自我描述的语句

相关文章:

评论:

Wei
在 2012-02-24 12:30 写道:

不错,不错,也给楼主推荐两个 图片文字识别的软件:CAJViewer,汉王PDF OCR,能将图片上的字转成文件

回复
胡阳
在 2012-06-20 12:42 写道:

相当nice,可惜后面的东西没详细介绍。不过还是谢谢博主。

回复
胡阳
在 2012-06-20 12:44 写道:

另外,如果不开源的话,有没有考虑做一个对外的接口?

回复
小子
在 2012-08-14 08:04 写道:

那个在线工具制表符神马的也出来了

回复
skip
在 2012-11-22 11:47 写道:

很有用。。。。

回复
jhack
在 2013-04-17 10:04 写道:

最近要用到这个项目。就搜索。结果163博客有一篇同样的文章,没有写转载,看后没有最后一个图,觉得是个假的……后又搜索。在CSDN上找到了一篇文章。写的是原创。研究后发现个问题……CSDN相同内容的聚合……有点抄袭嫌疑……之前用的是百度搜索的。后来换成谷歌,你这篇就出来了……深恨那些转载又说是自己原创的人……
有几点想问作者。你的站在在哪里架着。我用的sae python 结果没有pygame包。就想知道作者的站是在哪里架着的gae吗?
另外,我想知道后面的内容。就是标点。英文。换行方面的内容……希望可以给点指点……本人刚学python。还很菜。若是有些许demo 不胜感激。
77jh@sina.com 是我的邮箱。

回复
oldj
在 2013-04-22 17:05 写道:

多谢支持,呵呵。

我的站点在国外一个VPS上,可以自己随意安装软件或者包。关于换行等的内容邮件发你了。

回复
cwazjz
在 2013-05-10 10:15 写道:

也发给我一份吧!跪求!谢谢~~1255987643@qq.com

回复
cw
在 2013-04-24 15:24 写道:

File "D:Python27libsite-packagesPILImageFont.py", line 34, in getattr
raise ImportError("The _imagingft C module is not installed")
ImportError: The _imagingft C module is not installed 用XP运行的总是报这个错误,能告诉我是怎么回事吗谢谢

回复
oldj
在 2013-04-24 17:08 写道:

你装的是PIL 1.1.7吧?换成PIL 1.1.6试试?

回复
cw
在 2013-04-25 09:23 写道:

Traceback (most recent call last):
File "C:Documents and SettingsAdministrator桌面复件 2.py", line 7, in
font = ImageFont.truetype(os.path.join("fonts", "simsun.ttc"), 18)
File "D:Python26Libsite-packagesPILImageFont.py", line 214, in truetype
return FreeTypeFont(filename, size, index, encoding)
File "D:Python26Libsite-packagesPILImageFont.py", line 121, in init
self.font = _imagingft.getfont(file, size, index, encoding)
IOError: cannot open resource还是报错 你有在XP安装的所有包和步骤吗?能不能发我一份?谢谢

回复
cwazjz
在 2013-05-10 08:59 写道:

后来重装了一下再装的PIL1.1.6就可以了谢谢,后面有长文本换行问题、英文单词断字问题、标点符号换行问题等的一些介绍吗? 怎么换行 能讲解一下吗?谢谢!!

回复
FMN
在 2013-06-04 09:50 写道:

博主你好,本人喜欢python,可是深受python的编码问题困扰,就连学习博主的这篇文章都遇到了编码问题....
python脚本用记事本另存问utf-8格式,顶部也加上了#coding:utf-8,可是运行出错了:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
把text=u'测试'改为text='测试'就不会出错,可是会出来的文字是乱码~~~

最近在学习python,所以在sae python上做了一个图片分享小站:yesyouknow.sinaapp.com
想给图片做文字说明,可是读取网页上的中文也全部碰到了编码问题,照着网上说的方法都不行啊....楼主有木有什么方法给下提示......

回复
oldj
在 2013-06-06 16:14 写道:

顶部的注释写成“# -- coding: utf-8 --”试试呢?

另外,你是不是在中文版的Windows下运行的啊?中文版的Windows下终端的编码是GBK之类的,输出UTF-8编码的文字可能会是乱码,如果要在终端输出汉字可以转一下码,比如:

print u"汉字".encode("gbk")

或者也可以试试这样:

import locale import codecs

print locale.getpreferredencoding();
取得系统默认编码

encoding = codecs.lookup(locale.getpreferredencoding()).name

print u"汉字".encode(encoding)

回复
ZHONGHAO
在 2014-04-15 04:43 写道:

你好,博主。我在UBUNTU下面用你的代码想要生成图片,但是出来的片是空白。我查了下可能是对中文字体支持的不好,我的UBUNTU是英文的,需要把它语言设置成中文才行吗?谢谢。搜了很多,没找到解决方案。

回复
oldj
在 2014-04-15 10:13 写道:

和系统语言没有关系,我也在英文版的Ubuntu下运行过,只要指定所用的字体文件的路径就行。是不是你的字体文件路径有误?或者字体本身有问题?换一个字体试试呢?

回复
ZHONGHAO
在 2014-04-15 11:44 写道:

谢谢,问题解决了。之前我随便找了个网站下载的字体。刚才我直接从系统里拷贝了字体,能运行。多谢

回复
Jenny
在 2014-05-20 16:58 写道:

可不可以把关于如何换行的办法告诉我下。 发我邮箱吧 342542651@qq.com

回复
beio
在 2016-03-08 16:49 写道:

大神,我照着你的代码写得,在IO那一块 读取出错,但是不影响使用
只是在换行等问题的处理上不是很明白,还望解答。谢谢

回复
sora
在 2016-06-09 16:38 写道:

求换行处理代码 406423038@qq.com

回复
lamont
在 2016-11-24 14:25 写道:

博主,您好。
我使用的Python3.5.2,按照文中第4部分,执行到第22行pygame.image.save(rtext, sio),出现了编码方面的错误信息:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 12: invalid continuation byte
另外还有两种重复出现的错误信息,摘录如下。
第一种,出现于开始和结束部分循环了好几屏:
During handling of the above exception, another exception occurred:
SystemError: returned a result with an error set
第二种,在中间连续循环出现了好几屏:
During handling of the above exception, another exception occurred:
SystemError: returned a result with an error set

请问在 python3 中该如何使用 StringIO 实现文中第4部分的程序?

回复
lamont
在 2016-11-24 14:33 写道:

抱歉,重复出现的错误信息有遗漏,补发一下(这回不管排版了,原样截取吧):
第一种:
During handling of the above exception, another exception occurred:

SystemError Traceback (most recent call last)
SystemError: returned a result with an error set
第二种:
During handling of the above exception, another exception occurred:

SystemError Traceback (most recent call last)
SystemError: returned a result with an error set

回复
oldj
在 2016-11-24 15:02 写道:

确实,Python 3 里那段代码会出错,我也暂时没有找到解决办法…

不过这个方案是几年前的,主要是处理当时 PIL 库不能正常处理小号点阵字体的问题,现在想把文字转图片的话,直接用最新的 Pillow 库就可以(代码类似于第 1 部分的方案),不用再用 pygame 转一下了。

回复
小鬼
在 2017-04-30 23:31 写道:

博主你好,求换行处理代码 ***@qq.com,谢谢。

回复
a392623565
在 2017-05-15 11:34 写道:

楼主是否可以分享一下 文字处理的代码。
***@企鹅.com

回复
ssshen
在 2017-08-31 15:28 写道:

新版的pillow4.2.1 已经没有 示例1中的情况了,可以正常显示汉字

回复
oldj
在 2017-08-31 15:31 写道:

好的,谢谢告知!

回复
mike
在 2017-10-13 10:06 写道:

博主你好,求换行处理的代码。后面文章处理得很精美。***@qq.com
多谢!

回复
oldj
在 2017-10-25 20:10 写道:

核心代码见 https://gist.github.com/oldj/9c4d012d6fff059ccea7 ,主要是 makeLongLineToLines 等方法。

回复
kli
在 2017-10-25 09:54 写道:

你好 博主 ,我也在尝试用相近的方法生成汉字图片,但是生成的图片尺寸不太对。宽与高不对等。例如
font = pygame.font.Font("simsun.ttc", 32)
rtext = font.render(x, True, (0, 0, 0), (255, 255, 255))
会生成32 33 3的图像。博主知道问题在哪吗

回复
HTS
在 2018-08-22 06:22 写道:

IOError: cannot open resource
是不是要先下字体?

回复

发表评论:

电子邮件地址不会被公开。 必填项已用 * 标注。