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

在 Python 中,PIL (Python Imaging Library) 是最常用的绘图库,自然地,尝试从 PIL 开始。
1、使用 PIL 将文字转换为图片
说转换其实并不恰当,真实的过程是:先在内存中生成一张图片,将需要的文字绘制到这个图片上,再将图片保存到指定位置。代码如下:
04 | import Image, ImageFont, ImageDraw |
06 | text = u "这是一段测试文本,test 123。" |
08 | im = Image.new( "RGB" , ( 300 , 50 ), ( 255 , 255 , 255 )) |
09 | dr = ImageDraw.Draw(im) |
10 | font = ImageFont.truetype(os.path.join( "fonts" , "msyh.ttf" ), 14 ) |
12 | dr.text(( 10 , 5 ), text, font = font, fill = "#000000" ) |
当然,要使上面的代码能正常运行,你还需要在代码目录建一个“fonts”文件夹,并将字体文件“msyh.ttf”(微软雅黑)放在里面。最终生成的图片效果如下:

从上面可以看到,使用 PIL 把文本转为图片是非常方便的。当然,上面还只是一个简单的例子,还没有考虑长文本换行的问题。
2、使用点阵字体
对于大多数屏幕阅读来说,较小的字号(<= 14px)使用点阵字体来说更加清晰和便于阅读,例如 Windows 中文版下的默认字体宋体在字号较小时就通常被渲染成点阵字体。那么,我们是否可以在生成的图片中使用点阵字体呢?
修改上面的代码的第 10 行,将字体从微软雅黑(msyh.ttf)换成了宋体(simsun.ttc),其它保持不变。
1 | font = ImageFont.truetype(os.path.join( "fonts" , "simsun.ttc" ), 14 ) |
生成的图片如下:

杯具发生了,汉字没有正常显示!
网上搜索了一圈,发现这好像是 PIL 的一个 bug,PIL 目前的版本中,不能正确处理非 ASCII 字符的点阵字体的渲染。对于像宋体这样的字体来说,只有 >= 18px 时,才会被当作矢量字体处理,也就是说只有当字体 >= 18px 时,文字才能正常显示:
1 | font = ImageFont.truetype(os.path.join( "fonts" , "simsun.ttc" ), 18 ) |
效果如下:

增大字体虽然解决了汉字不能正常显示的问题,但还是没有解决我们一开始的初衷:使用点阵字体进行渲染。但是,这个目标使用现阶段的 PIL 似乎有点难以实现了。
3、使用 pyGame 渲染点阵字体
Python 的第三方模块或组件非常多,可用来绘图的除了 PIL 之外,就还有 Pycairo、matplotlib、pyGame 等。在这儿,我使用 pyGame 来完成点阵字体的渲染工作。
代码如下:
08 | text = u "这是一段测试文本,test 123。" |
09 | font = pygame.font.Font(os.path.join( "fonts" , "simsun.ttc" ), 14 ) |
10 | rtext = font.render(text, True , ( 0 , 0 , 0 ), ( 255 , 255 , 255 )) |
12 | pygame.image.save(rtext, "t.jpg" ) |
效果如下:

可以看到,使用 pyGame ,点阵字体的问题终于搞定了。
4、结合 PIL 和 pyGame
pyGame 虽然可以解决点阵字体的渲染问题,但讲到对图片的处理,还是 PIL 更为强大。那么,我们为什么不把两者结合起来呢?用 pyGame 渲染点阵字体,然后用 PIL 生成整张图片。
代码如下:
05 | import Image, ImageFont, ImageDraw |
10 | text = u "这是一段测试文本,test 123。" |
12 | im = Image.new( "RGB" , ( 300 , 50 ), ( 255 , 255 , 255 )) |
15 | font = pygame.font.Font(os.path.join( "fonts" , "simsun.ttc" ), 14 ) |
18 | rtext = font.render(text, True , ( 0 , 0 , 0 ), ( 255 , 255 , 255 )) |
21 | sio = StringIO.StringIO() |
22 | pygame.image.save(rtext, sio) |
25 | line = Image. open (sio) |
26 | im.paste(line, ( 10 , 5 )) |
原理很简单,先将文字用 pyGame 渲染为图片,将渲染结果保存在一个 StringIO 对象中,然后再用 PIL 加载它。使用 StringIO 的好处是,一切操作都是在内存中进行的,不需要先将它保存到硬盘再用 PIL 读取,因为硬盘 IO 的效率相对来说是比较低的。
最终效果如下:

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

最后,欢迎试用我写的在线文本转图片工具:txt2.im 。:-)