Last active
April 6, 2024 11:17
-
-
Save oldj/9c4d012d6fff059ccea7 to your computer and use it in GitHub Desktop.
Revisions
-
oldj revised this gist
Jun 25, 2022 . No changes.There are no files selected for viewing
-
oldj revised this gist
Oct 25, 2017 . No changes.There are no files selected for viewing
-
oldj revised this gist
Oct 25, 2017 . No changes.There are no files selected for viewing
-
oldj revised this gist
Oct 25, 2017 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,8 +8,8 @@ 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__)) -
oldj created this gist
Jan 6, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,328 @@ # -*- coding: utf-8 -*- # # Author: oldj # Email: [email protected] # Blog: http://oldj.net # import os import re import StringIO import Image 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()