利用Python如何生成便签图片详解

前言

最近有文字转图片的需求,但是不太想下载 APP,就使用 Python Pillow 实现了一个,效果如下:

PIL 提供了 PIL.ImageDraw.ImageDraw.text方法,可以方便的把文字写到图片上,简单示例如下:

from PIL import Image, ImageDraw, ImageFont

# get an image

base = Image.open('Pillow/Tests/images/hopper.png').convert('RGBA')

# make a blank image for the text, initialized to transparent text color

txt = Image.new('RGBA', base.size, (255,255,255,0))

# get a font

fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40)

# get a drawing context

d = ImageDraw.Draw(txt)

# draw text, half opacity

d.text((10,10), "Hello", font=fnt, fill=(255,255,255,128))

# draw text, full opacity

d.text((10,60), "World", font=fnt, fill=(255,255,255,255))

out = Image.alpha_composite(base, txt)

out.show()

为什么要计算文字的宽高呢?把文字直接写到背景图不可以么?

Pillow PIL.ImageDraw.ImageDraw.text 写文字是按换行符 \n 换行的,如果个字符串特别长,文字部分就会超出背景图的宽度,所以第一步我们需要先把文本按固定的宽度计算出高度。

像图上写的这样,文字转图片分三步:

计算文字宽高

生成响应尺寸背景图

把文字写到图片上

计算文字宽高

这里背景图宽度是固定的,所以文字的宽可以不用计算。 PIL.ImageDraw.ImageDraw.text是通过 \n 来换行的,那我们只需要在文字合适的位置加上 \n 就可以了。

第一个想到的是 textwrap 方法,textwrap 可以实现通过调整换行符的位置来格式化文本。但 textwrap 还有一个问题就是它是根据字符长度来分隔的,但文本中的字符并不是等宽的,通过 textwrap 格式化后的文字写到图片上效果可能是这样的:

使用这种方式,如果我们要调整字体大小,每一行的长度都还需要再重新调整。

为了保证每一行宽度尽可能的一致,这里使用 PIL.ImageDraw.ImageDraw.textsize 获取字符宽高,然后按约定宽度把长文本分隔成文本列表,然后把列表每行文字写到图片上。

def get_paragraph(text, note_width):

# 把每段文字按约定宽度分隔成几行

txt = Image.new('RGBA', (100, 100), (255, 255, 255, 0))

# get a drawing context

draw = ImageDraw.Draw(txt)

paragraph, sum_width = '', 0

line_numbers, line_height = 1, 0

for char in text:

w, h = draw.textsize(char, font)

sum_width += w

if sum_width > note_width:

line_numbers += 1

sum_width = 0

paragraph += '\n'

paragraph += char

line_height = max(h, line_height)

if not paragraph.endswith('\n'):

paragraph += '\n'

return paragraph, line_height, line_numbers

def split_text(text):

# 将文本按规定宽度分组

max_line_height, total_lines = 0, 0

paragraphs = []

for t in text.split('\n'):

# 先按 \n 把文本分段

paragraph, line_height, line_numbers = get_paragraph(t)

max_line_height = max(line_height, max_line_height)

total_lines += line_numbers

paragraphs.append((paragraph, line_numbers))

line_height = max_line_height

total_height = total_lines * line_height

# 这里返回分好的段,文本总高度以及行高

return paragraphs, total_height, line_height

这是按字符宽度分隔文本写到图片的效果:

由于文本长度不固定,生成得到的文本高度也不固定,背景图我们也需要动态生成

根据文本高度生成背景图

通过图片我们可以看到,头部和尾部是固定的,变化的是文字部分,那么背景图片的高度计算公式为

背景图片高度=头部高度+尾部高度+文本高度

实现代码如下:

NOTE_HEADER_IMG = path.normpath(path.join(

path.dirname(__file__), 'note_header_660.png'))

NOTE_BODY_IMG = path.normpath(path.join(

path.dirname(__file__), 'note_body_660.png'))

NOTE_FOOTER_IMG = path.normpath(path.join(

path.dirname(__file__), 'note_footer_660.png'))

NOTE_WIDTH = 660

NOTE_TEXT_WIDTH = 460

body_height = NOTE_BODY_HEIGHT = 206

header_height = NOTE_HEADER_HEIGHT = 89

footer_height = NOTE_FOOTER_HEIGHT = 145

font = ImageFont.truetype(NOTE_OTF, 24)

def get_images(note_height):

numbers = note_height // body_height + 1

images = [(NOTE_HEADER_IMG, header_height)]

images.extend([(NOTE_BODY_IMG, body_height)] * numbers)

images.append((NOTE_FOOTER_IMG, footer_height))

return images

def make_backgroud():

# 将图片拼接到一起

images = get_images()

total_height = sum([height for _, height in images])

# 最终拼接完成后的图片

backgroud = Image.new('RGB', (body_width, total_height))

left, right = 0, 0

background_img = '/tmp/%s_backgroud.png' % total_height

# 判断背景图是否存在

if path.exists(background_img):

return background_img

for image_file, height in images:

image = Image.open(image_file)

# (0, left, self.body_width, right+height)

# 分别为 左上角坐标 0, left

# 右下角坐标 self.body_width, right+height

backgroud.paste(image, (0, left, body_width, right+height))

left += height # 从上往下拼接,左上角的纵坐标递增

right += height # 左下角的纵坐标也递增

backgroud.save(background_img, quality=85)

return background_img

将文字写到图片

现在我们得到了背景图以及分隔好的文本,就可以直接将文本写到图片上了

def draw_text(paragraphs, height):

background_img = make_backgroud()

note_img = Image.open(background_img).convert("RGBA")

draw = ImageDraw.Draw(note_img)

# 文字开始位置坐标,需要根据背景图的大小做调整

x, y = 80, 100

for paragraph, line_numbers in paragraphs:

for line in paragraph.split('\n')[:-1]:

draw.text((x, y), line, fill=(110, 99, 87), font=font)

y += line_height

# draw.text((x, y), paragraph, fill=(110, 99, 87), font=font)

# y += self.line_height * line_numbers

note_img.save(filename, "png", quality=1, optimize=True)

return filename

完整版代码请查看 [ https://github.com/gusibi/momo/blob/master/momo/note.py ]

执行后效果如图:

遇到的问题

为了能方便使用,我把这个做成了公号的一个功能,然后遇到了一个严重问题, 太慢了!

使用 line_profiler 分析可以发现,大部分时间都消耗在了图片保存这一步,

note_img.save(filename, "png", quality=1, optimize=True)

性能分析工具也会占用时间,测试完成后需要关闭分析

解决这个问题可能的方法:

减小背景图片大小

减小字体大小

通过测试,发现把背景图宽度从990减到660,字体大小从40px 调整到24px,生成的图片大小体积缩小了接近1倍,生成速度也比原来快了2/5。

相同代码,相同文本,使用 python3 只用了2.3s,而 Python2 用时却是5.3 s,还从来没在其它功能上遇到过 Python2 和 Python3 有这么大的差别。

具体差异可以使用源码测试一下

还是有问题

优化完图片生成速度后,发现在长文本状态下,公号还是会超时报错。经过检查发现是图片上传到公众平台太慢了(服务器只有1M 带宽,没有办法.)。

解决方法,把图片上传到腾讯云(文件上传使用的是内网带宽,不受限制),返回图片 url。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对码农之家的支持。

以上就是本次给大家分享的关于java的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

python创建方法draw_Python生成便签图片的方法总结相关推荐

  1. js实现word生成书签_js生成word中图片处理方法

    首先功能是要求前台导出word,但是前后台是分离的,图片存在后台,所以就存在跨域问题. 导出文字都是没有问题的(jquery.wordexport.js),但是导出图片就存在问题了: 图片是以链接形式 ...

  2. python处理文本文件实现生成指定格式文件的方法

    python处理文本文件实现生成指定格式文件的方法 关于python处理文本文件实现生成指定格式文件,很多朋友都不知道该怎么弄. 下面是具体代码,希望对大家有帮助 可以在下面复制: import os ...

  3. 小米手机便签怎么看写了多少字 小米手机便签统计字数方法

    在小米手机上,无论用的是系统自带便签工具,还是云便签,都可以用它们来记录文字,并且每条便签内容还可以实时查看已添加的文字字数.那具体小米手机便签怎么看写了多少字?小米手机便签统计字数方法是什么? 一. ...

  4. 人人都能轻松做到的便签整理术方法

    生活和工作中的很多事情,都可以通过有效的整理,来提升执行舒适度和做事效率.就拿工作中的任务事项来说,在每天工作开始前,职场人士可使用云便签敬业签记录当天的工作任务,并对其安排合理的完成时间,来规划整理 ...

  5. Python 获得pdf中的文字、图片文字方法

    Python 获得pdf中的文字.图片文字方法 下载word版文件 OCR,全称Optical character recognition,中文译名叫做光学文字识别.它把图像中的字符,转换为机器编码的 ...

  6. 安卓手机云便签图片录音及附件文件的存储位置在哪里?

    在敬业签云便签中记录的文字.图片.音频以及附件文件等内容默认存储位置是云端服务器空间中,安卓手机端可以将便签图片.录音音频或者附件等内容资料下载保存到本地,以华为手机为例,存储位置如下: 一.打开手机 ...

  7. php长图生成,用 3 行代码就可以生成好看的锤子便签图片(传入内容即可,俗称长微博)...

    因为自己的站点需要生成长微博的服务,就用 php 做了一个 composer 上的包,并开源出来了~有需要的自己拿去~3 行代码调用即可.生成的图片仿锤子便签的~ php 文字生成图片(使用 comp ...

  8. python创建窗体_python生成窗口

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 2.python生成目录树上述 cmd 方式虽然可以生成目录树,但是并不美观,让 ...

  9. python将图片保存到excel_使用Python导出Excel图表以及导出为图片的方法

    本篇讲下如何使用纯python代码将excel 中的图表导出为图片.这里需要使用的模块有win32com.pythoncom模块. 网上经查询有人已经写好的模块pyxlchart,具体代码如下: fr ...

最新文章

  1. Enterprise Library——企业库配置管理应用程序块
  2. C++ Primer 5th笔记(chap 17 标准库特殊设施)指定浮点数记数法
  3. java 中的chartdata_获取Helm Charts中的文件夹列表
  4. fifo页面置换算法设计思路_千万级并发!如何设计一个多级缓存系统?
  5. python rpc webservice_关于RPC、HTTP、WebService的区别
  6. cmake编译安装MySQL5.5.32
  7. 创建目录_聊聊Word创建目录那些事儿
  8. 稳定土厂拌设备控制系统-基本介绍(图)
  9. NUC1921 E.迷雾森林【筛选法】
  10. 如何在iPhone和Android上使用Instagram效果
  11. 使用openwrt-SDK 生成IPK
  12. python从excel读取数据用matplotlib画平面折线图
  13. python爬虫区划代码表
  14. C#如何在Windows上接入蓝牙设备
  15. 天威TM1668芯片官方资料,讲解,代码,自己写的例程
  16. Debian 12 Bookworm 尝鲜记
  17. C语言中运算符的优先级排序表
  18. 001软件测试基本介绍
  19. 2. 因子(factor)、缺失数据(na)、字符串、时间序列数据
  20. THREE.js模型贴图不显示

热门文章

  1. python编辑器安装视频_基于 Python 的视频编辑器——MoviePy
  2. makefile eval函数详解
  3. 解决:QQ能上网,网页打不开
  4. easyui combotree(树形下拉框)使用收获总结
  5. Python爬虫案例-猪八戒
  6. 写出小说滑动翻页的效果
  7. 快速选择算法Quick Select Algorithm(Java和C++代码实现)
  8. 知识图谱 - 使用图数据库(记一次eno4j搭建分享)
  9. 【Flink】FlinkSQL代码格式问题
  10. outlook服务器文件夹,关于在我的电脑上文件夹Outlook for Mac