1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
# -*- coding: utf-8 -*-
import base64
import io
import os
from typing import List, Tuple, TypedDict
from django.conf import settings
from PIL import Image, ImageDraw, ImageFont
from PIL.ImageFont import FreeTypeFont
class TextSplitter:
"""将文本按照指定宽度进行拆分"""
def __init__(self, font: FreeTypeFont, max_width: float):
self.font = font
self.max_width = max_width
def split_word(self, word: str):
"""
将单词按照最大宽度进行拆分
"""
if self.font.getlength(word) <= self.max_width:
return [word]
parts = []
current_part = ""
for char in word:
current_part += char
if self.font.getlength(current_part) > self.max_width:
parts.append(current_part[:-1])
current_part = char
parts.append(current_part)
return parts
def split_text(self, text: str) -> List[str]:
"""
按照最大宽度对文本进行拆分,优先将单词完整地放在一行,如果单词太长再考虑从中间切开
"""
words = text.split(" ")
lines = []
current_line = ""
for word in words:
if not current_line:
current_line = word
elif self.font.getlength(f"{current_line} {word}") <= self.max_width:
current_line += " " + word
else:
lines.append(current_line)
current_line = word
# 避免行过长
if self.font.getlength(current_line) > self.max_width:
parts = self.split_word(current_line)
lines.extend(parts[:-1])
# 保留最后一个切片用于下次拼接
current_line = parts[-1]
if current_line:
lines.append(current_line)
return lines
class Params(TypedDict):
plaintext: str
background_color: Tuple[int, int, int, int]
image_width: int
image_height: int
padding: int
font_size: int
font_color: Tuple[int, int, int, int]
rotation_angle: float
def generate(params: Params) -> str:
"""生成明水印"""
# 获取配置信息
plaintext = params["plaintext"]
background_color = params["background_color"]
image_width = params["image_width"]
image_height = params["image_height"]
padding = params["padding"]
font_size = params["font_size"]
font_color = params["font_color"]
rotation_angle = params["rotation_angle"]
font = ImageFont.truetype(os.path.join(settings.BASE_DIR, "static/font/SourceHanSerifCN-Regular.ttf"), font_size)
# 生成水印
text_width, text_height = font.getsize(plaintext)
blank = Image.new("RGBA", (image_width, image_height), background_color)
draw = ImageDraw.Draw(blank)
# 拆分文本为多行
multiline_text = TextSplitter(font, blank.width).split_text(plaintext)
# 计算每行文本的高度和总高度
total_height = text_height * len(multiline_text)
# 逐行绘制文本
for i, line in enumerate(multiline_text):
line_width, _ = font.getsize(line)
x = (blank.width - line_width) / 2
y = (blank.height - total_height) / 2 + i * text_height
draw.text((x, y), line, font=font, fill=font_color)
# 裁剪出文字
box = [
0,
total_height / 2,
blank.width,
blank.height / 2 + total_height / 2,
]
text_im = blank.crop(box)
# 旋转后裁剪大小
text_rotate = text_im.rotate(rotation_angle, expand=True)
text_rotate = text_rotate.resize((image_width, image_height), Image.ANTIALIAS)
# 填充边距
img = Image.new("RGBA", (image_width + padding, image_height + padding), background_color)
paste_box = (int(padding / 2), int(padding / 2))
img.paste(text_rotate, paste_box)
img = img.resize((image_width, image_height), Image.ANTIALIAS)
# 将图片转换为base64字符串
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode("utf8")
def test() -> str:
return generate(
Params(
plaintext="Raja abcdefghijklmnopqrstuvwxyz This is a long text 这是一段很长的文本 这是一段很长的文本",
background_color=(255, 255, 255, 0),
image_width=260,
image_height=165,
padding=40,
font_size=16,
font_color=(0, 0, 0, 50),
rotation_angle=30,
)
)
|