Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# -*- coding: utf-8 -*-
#
# Author: oldj
# Email: oldj.wu@gmail.com
# Blog: http://oldj.net
#
import os
import re
import StringIO
from PIL import Image
from PIL import ImageDraw
import pygame
g_script_folder = os.path.dirname(os.path.abspath(__file__))
g_fonts_folder = os.path.join(g_script_folder, "fonts")
g_re_first_word = re.compile((u""
+ u"(%(prefix)s+\S%(postfix)s+)" # 标点
+ u"|(%(prefix)s*\w+%(postfix)s*)" # 单词
+ u"|(%(prefix)s+\S)|(\S%(postfix)s+)" # 标点
+ u"|(\d+%%)" # 百分数
) % {
"prefix": u"['\"\(<\[\{‘“(《「『]",
"postfix": u"[:'\"\)>\]\}:’”)》」』,;\.\?!,、;。?!]",
})
pygame.init()
def getFontForPyGame(font_name="wqy-zenhei.ttc", font_size=14):
return pygame.font.Font(os.path.join(g_fonts_folder, font_name), font_size)
def makeConfig(cfg=None):
if not cfg or type(cfg) != dict:
cfg = {}
default_cfg = {
"width": 440, # px
"padding": (15, 18, 20, 18),
"line-height": 20, #px
"title-line-height": 32, #px
"font-size": 14, # px
"title-font-size": 24, # px
"font-family": "wqy-zenhei.ttc",
# "font-family": "msyh.ttf",
"font-color": (0, 0, 0),
"font-antialiasing": True, # 字体是否反锯齿
"background-color": (255, 255, 255),
"border-size": 1,
"border-color": (192, 192, 192),
"copyright": u"本图文由 txt2.im 自动生成,但不代表 txt2.im 赞同其内容或立场。",
"copyright-center": False, # 版权信息居中显示,如为 False 则居左显示
"first-line-as-title": True,
"break-word": False,
}
default_cfg.update(cfg)
return default_cfg
def makeLineToWordsList(line, break_word=False):
u"""将一行文本转为单词列表"""
if break_word:
return [c for c in line]
lst = []
while line:
ro = g_re_first_word.match(line)
end = 1 if not ro else ro.end()
lst.append(line[:end])
line = line[end:]
return lst
def makeLongLineToLines(long_line, start_x, start_y, width, line_height, font, cn_char_width=0):
u"""将一个长行分成多个可显示的短行"""
txt = long_line
# txt = u"测试汉字abc123"
# txt = txt.decode("utf-8")
if not txt:
return [None]
words = makeLineToWordsList(txt)
lines = []
if not cn_char_width:
cn_char_width, h = font.size(u"")
avg_char_per_line = width / cn_char_width
if avg_char_per_line <= 1:
avg_char_per_line = 1
line_x = start_x
line_y = start_y
while words:
tmp_words = words[:avg_char_per_line]
tmp_ln = "".join(tmp_words)
w, h = font.size(tmp_ln)
wc = len(tmp_words)
while w < width and wc < len(words):
wc += 1
tmp_words = words[:wc]
tmp_ln = "".join(tmp_words)
w, h = font.size(tmp_ln)
while w > width and len(tmp_words) > 1:
tmp_words = tmp_words[:-1]
tmp_ln = "".join(tmp_words)
w, h = font.size(tmp_ln)
if w > width and len(tmp_words) == 1:
# 处理一个长单词或长数字
line_y = makeLongWordToLines(
tmp_words[0], line_x, line_y, width, line_height, font, lines
)
words = words[len(tmp_words):]
continue
line = {
"x": line_x,
"y": line_y,
"text": tmp_ln,
"font": font,
}
line_y += line_height
words = words[len(tmp_words):]
lines.append(line)
if len(lines) >= 1:
# 去掉长行的第二行开始的行首的空白字符
while len(words) > 0 and not words[0].strip():
words = words[1:]
return lines
def makeLongWordToLines(long_word, line_x, line_y, width, line_height, font, lines):
if not long_word:
return line_y
c = long_word[0]
char_width, char_height = font.size(c)
default_char_num_per_line = width / char_width
while long_word:
tmp_ln = long_word[:default_char_num_per_line]
w, h = font.size(tmp_ln)
l = len(tmp_ln)
while w < width and l < len(long_word):
l += 1
tmp_ln = long_word[:l]
w, h = font.size(tmp_ln)
while w > width and len(tmp_ln) > 1:
tmp_ln = tmp_ln[:-1]
w, h = font.size(tmp_ln)
l = len(tmp_ln)
long_word = long_word[l:]
line = {
"x": line_x,
"y": line_y,
"text": tmp_ln,
"font": font,
}
line_y += line_height
lines.append(line)
return line_y
def makeMatrix(txt, font, title_font, cfg):
width = cfg["width"]
data = {
"width": width,
"height": 0,
"lines": [],
}
a = txt.split("\n")
cur_x = cfg["padding"][3]
cur_y = cfg["padding"][0]
cn_char_width, h = font.size(u"")
for ln_idx, ln in enumerate(a):
ln = ln.rstrip()
if ln_idx == 0 and cfg["first-line-as-title"]:
f = title_font
line_height = cfg["title-line-height"]
else:
f = font
line_height = cfg["line-height"]
current_width = width - cur_x - cfg["padding"][1]
lines = makeLongLineToLines(ln, cur_x, cur_y, current_width, line_height, f, cn_char_width=cn_char_width)
cur_y += line_height * len(lines)
data["lines"].extend(lines)
data["height"] = cur_y + cfg["padding"][2]
return data
def makeImage(data, cfg):
u"""
"""
width, height = data["width"], data["height"]
if cfg["copyright"]:
height += 48
im = Image.new("RGB", (width, height), cfg["background-color"])
dr = ImageDraw.Draw(im)
for ln_idx, line in enumerate(data["lines"]):
__makeLine(im, line, cfg)
# dr.text((line["x"], line["y"]), line["text"], font=font, fill=cfg["font-color"])
# 缩放
# im = im.resize((width / 2, height / 2), Image.ANTIALIAS)
drawBorder(im, dr, cfg)
drawCopyright(im, dr, cfg)
return im
def drawCopyright(im, dr, cfg):
u"""绘制版权信息"""
if not cfg["copyright"]:
return
font = getFontForPyGame(font_name=cfg["font-family"], font_size=12)
rtext = font.render(cfg["copyright"],
cfg["font-antialiasing"], (128, 128, 128), cfg["background-color"]
)
sio = StringIO.StringIO()
pygame.image.save(rtext, sio)
sio.seek(0)
copyright_im = Image.open(sio)
iw, ih = im.size
cw, ch = rtext.get_size()
padding = cfg["padding"]
offset_y = ih - 32 - padding[2]
if cfg["copyright-center"]:
cx = (iw - cw) / 2
else:
cx = cfg["padding"][3]
cy = offset_y + 12
dr.line([(padding[3], offset_y), (iw - padding[1], offset_y)], width=1, fill=(192, 192, 192))
im.paste(copyright_im, (cx, cy))
def drawBorder(im, dr, cfg):
u"""绘制边框"""
if not cfg["border-size"]:
return
w, h = im.size
x, y = w - 1, h - 1
dr.line(
[(0, 0), (x, 0), (x, y), (0, y), (0, 0)],
width=cfg["border-size"],
fill=cfg["border-color"],
)
def __makeLine(im, line, cfg):
if not line:
return
sio = StringIO.StringIO()
x, y = line["x"], line["y"]
text = line["text"]
font = line["font"]
rtext = font.render(text, cfg["font-antialiasing"], cfg["font-color"], cfg["background-color"])
pygame.image.save(rtext, sio)
sio.seek(0)
ln_im = Image.open(sio)
im.paste(ln_im, (x, y))
def txt2im(txt, outfn, cfg=None, show=False):
# print(cfg)
cfg = makeConfig(cfg)
# print(cfg)
font = getFontForPyGame(cfg["font-family"], cfg["font-size"])
title_font = getFontForPyGame(cfg["font-family"], cfg["title-font-size"])
data = makeMatrix(txt, font, title_font, cfg)
im = makeImage(data, cfg)
im.save(outfn)
if os.name == "nt" and show:
im.show()
def test():
c = open("test.txt", "rb").read().decode("utf-8")
txt2im(c, "test.png", show=True)
if __name__ == "__main__":
test()
@oldj

This comment has been minimized.

Copy link
Owner Author

oldj commented Oct 25, 2017

@youqingkui

This comment has been minimized.

Copy link

youqingkui commented Apr 22, 2018

碰到点问题,python3好像不行

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.