✨ 父级插件加载

This commit is contained in:
HibiKier
2024-08-30 23:50:45 +08:00
parent 0074483280
commit a3681216bd
9 changed files with 177 additions and 153 deletions
+57 -59
View File
@@ -1,15 +1,17 @@
import base64
import math
import uuid
import base64
import itertools
import contextlib
from io import BytesIO
from pathlib import Path
from typing import Literal, Tuple, TypeAlias, overload
from typing_extensions import Self
from typing import Literal, TypeAlias, overload
from nonebot.utils import run_sync
from PIL import Image, ImageDraw, ImageFilter, ImageFont
from PIL.Image import Image as tImage
from PIL.ImageFont import FreeTypeFont
from typing_extensions import Self
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from zhenxun.configs.path_config import FONT_PATH
@@ -18,7 +20,7 @@ ModeType = Literal[
]
"""图片类型"""
ColorAlias: TypeAlias = str | Tuple[int, int, int] | Tuple[int, int, int, int] | None
ColorAlias: TypeAlias = str | tuple[int, int, int] | tuple[int, int, int, int] | None
CenterType = Literal["center", "height", "width"]
"""
@@ -52,9 +54,7 @@ class BuildImage:
self.height = height
self.color = color
self.font = (
self.load_font(font, font_size)
if not isinstance(font, FreeTypeFont)
else font
font if isinstance(font, FreeTypeFont) else self.load_font(font, font_size)
)
if background:
if isinstance(background, bytes):
@@ -66,14 +66,14 @@ class BuildImage:
else:
self.width = self.markImg.width
self.height = self.markImg.height
else:
if not width or not height:
raise ValueError("长度和宽度不能为空...")
elif width and height:
self.markImg = Image.new(mode, (width, height), color) # type: ignore
else:
raise ValueError("长度和宽度不能为空...")
self.draw = ImageDraw.Draw(self.markImg)
@property
def size(self) -> Tuple[int, int]:
def size(self) -> tuple[int, int]:
return self.markImg.size
@classmethod
@@ -94,9 +94,9 @@ class BuildImage:
text: str,
font: str | FreeTypeFont | Path = "HYWenHei-85W.ttf",
size: int = 10,
font_color: str | Tuple[int, int, int] = (0, 0, 0),
font_color: str | tuple[int, int, int] = (0, 0, 0),
color: ColorAlias = None,
padding: int | Tuple[int, int, int, int] | None = None,
padding: int | tuple[int, int, int, int] | None = None,
) -> Self:
"""构建文本图片
@@ -116,7 +116,7 @@ class BuildImage:
_font = None
if isinstance(font, FreeTypeFont):
_font = font
elif isinstance(font, (str, Path)):
elif isinstance(font, str | Path):
_font = cls.load_font(font, size)
width, height = cls.get_text_size(text, _font)
if isinstance(padding, int):
@@ -161,7 +161,7 @@ class BuildImage:
row_count = math.ceil(len(img_list) / row)
if row_count == 1:
background_width = (
sum([img.width for img in img_list]) + space * (row - 1) + padding * 2
sum(img.width for img in img_list) + space * (row - 1) + padding * 2
)
background_height = height * row_count + space * (row_count - 1) + padding * 2
background_image = cls(
@@ -189,20 +189,20 @@ class BuildImage:
返回:
FreeTypeFont: 字体
"""
path = FONT_PATH / font if type(font) == str else font
path = FONT_PATH / font if type(font) is str else font
return ImageFont.truetype(str(path), font_size)
@overload
@classmethod
def get_text_size(
cls, text: str, font: FreeTypeFont | None = None
) -> Tuple[int, int]: ...
) -> tuple[int, int]: ...
@overload
@classmethod
def get_text_size(
cls, text: str, font: str | None = None, font_size: int = 10
) -> Tuple[int, int]: ...
) -> tuple[int, int]: ...
@classmethod
def get_text_size(
@@ -210,7 +210,7 @@ class BuildImage:
text: str,
font: str | FreeTypeFont | None = "HYWenHei-85W.ttf",
font_size: int = 10,
) -> Tuple[int, int]:
) -> tuple[int, int]: # sourcery skip: remove-unnecessary-cast
"""获取该字体下文本需要的长宽
参数:
@@ -219,10 +219,10 @@ class BuildImage:
font_size: 字体大小
返回:
Tuple[int, int]: 长宽
tuple[int, int]: 长宽
"""
_font = font
if font and type(font) == str:
if font and type(font) is str:
_font = cls.load_font(font, font_size)
temp_image = Image.new("RGB", (1, 1), (255, 255, 255))
draw = ImageDraw.Draw(temp_image)
@@ -232,7 +232,8 @@ class BuildImage:
return text_width, text_height + 10
# return _font.getsize(str(text)) # type: ignore
def getsize(self, msg: str) -> Tuple[int, int]:
def getsize(self, msg: str) -> tuple[int, int]:
# sourcery skip: remove-unnecessary-cast
"""
获取文字在该图片 font_size 下所需要的空间
@@ -240,7 +241,7 @@ class BuildImage:
msg: 文本
返回:
Tuple[int, int]: 长宽
tuple[int, int]: 长宽
"""
temp_image = Image.new("RGB", (1, 1), (255, 255, 255))
draw = ImageDraw.Draw(temp_image)
@@ -252,11 +253,11 @@ class BuildImage:
def __center_xy(
self,
pos: Tuple[int, int],
pos: tuple[int, int],
width: int,
height: int,
center_type: CenterType | None,
) -> Tuple[int, int]:
) -> tuple[int, int]:
"""
根据居中类型定位xy
@@ -266,7 +267,7 @@ class BuildImage:
center_type: 居中类型
返回:
Tuple[int, int]: 定位
tuple[int, int]: 定位
"""
# _width, _height = pos
if self.width and self.height:
@@ -285,7 +286,7 @@ class BuildImage:
def paste(
self,
image: Self | tImage,
pos: Tuple[int, int] = (0, 0),
pos: tuple[int, int] = (0, 0),
center_type: CenterType | None = None,
) -> Self:
"""贴图
@@ -303,7 +304,6 @@ class BuildImage:
"""
if center_type and center_type not in ["center", "height", "width"]:
raise ValueError("center_type must be 'center', 'width' or 'height'")
width, height = 0, 0
_image = image
if isinstance(image, BuildImage):
_image = image.markImg
@@ -317,7 +317,7 @@ class BuildImage:
@run_sync
def point(
self, pos: Tuple[int, int], fill: Tuple[int, int, int] | None = None
self, pos: tuple[int, int], fill: tuple[int, int, int] | None = None
) -> Self:
"""
绘制多个或单独的像素
@@ -335,9 +335,9 @@ class BuildImage:
@run_sync
def ellipse(
self,
pos: Tuple[int, int, int, int],
fill: Tuple[int, int, int] | None = None,
outline: Tuple[int, int, int] | None = None,
pos: tuple[int, int, int, int],
fill: tuple[int, int, int] | None = None,
outline: tuple[int, int, int] | None = None,
width: int = 1,
) -> Self:
"""
@@ -358,13 +358,13 @@ class BuildImage:
@run_sync
def text(
self,
pos: Tuple[int, int],
pos: tuple[int, int],
text: str,
fill: str | Tuple[int, int, int] = (0, 0, 0),
fill: str | tuple[int, int, int] = (0, 0, 0),
center_type: CenterType | None = None,
font: FreeTypeFont | str | Path | None = None,
font_size: int = 10,
) -> Self:
) -> Self: # sourcery skip: remove-unnecessary-cast
"""
在图片上添加文字
@@ -382,11 +382,10 @@ class BuildImage:
异常:
ValueError: 居中类型错误
"""
text = str(text)
if center_type and center_type not in ["center", "height", "width"]:
raise ValueError("center_type must be 'center', 'width' or 'height'")
max_length_text = ""
sentence = text.split("\n")
sentence = str(text).split("\n")
for x in sentence:
max_length_text = x if len(x) > len(max_length_text) else max_length_text
if font:
@@ -398,7 +397,7 @@ class BuildImage:
ttf_w, ttf_h = self.getsize(max_length_text) # type: ignore
# ttf_h = ttf_h * len(sentence)
pos = self.__center_xy(pos, ttf_w, ttf_h, center_type)
self.draw.text(pos, text, fill=fill, font=font)
self.draw.text(pos, str(text), fill=fill, font=font)
return self
@run_sync
@@ -437,7 +436,7 @@ class BuildImage:
if not width and not height and not ratio:
raise ValueError("缺少参数...")
if self.width and self.height:
if not width and not height and ratio:
if not width and not height:
width = int(self.width * ratio)
height = int(self.height * ratio)
self.markImg = self.markImg.resize((width, height), Image.LANCZOS) # type: ignore
@@ -446,7 +445,7 @@ class BuildImage:
return self
@run_sync
def crop(self, box: Tuple[int, int, int, int]) -> Self:
def crop(self, box: tuple[int, int, int, int]) -> Self:
"""
裁剪图片
@@ -475,11 +474,10 @@ class BuildImage:
"""
self.markImg = self.markImg.convert("RGBA")
x, y = self.markImg.size
for i in range(n, x - n):
for k in range(n, y - n):
color = self.markImg.getpixel((i, k))
color = color[:-1] + (int(100 * alpha_ratio),)
self.markImg.putpixel((i, k), color)
for i, k in itertools.product(range(n, x - n), range(n, y - n)):
color = self.markImg.getpixel((i, k))
color = color[:-1] + (int(100 * alpha_ratio),)
self.markImg.putpixel((i, k), color)
self.draw = ImageDraw.Draw(self.markImg)
return self
@@ -492,7 +490,7 @@ class BuildImage:
buf = BytesIO()
self.markImg.save(buf, format="PNG")
base64_str = base64.b64encode(buf.getvalue()).decode()
return "base64://" + base64_str
return f"base64://{base64_str}"
def pic2bytes(self) -> bytes:
"""获取bytes
@@ -520,8 +518,8 @@ class BuildImage:
@run_sync
def rectangle(
self,
xy: Tuple[int, int, int, int],
fill: Tuple[int, int, int] | None = None,
xy: tuple[int, int, int, int],
fill: tuple[int, int, int] | None = None,
outline: str | None = None,
width: int = 1,
) -> Self:
@@ -543,8 +541,8 @@ class BuildImage:
@run_sync
def polygon(
self,
xy: list[Tuple[int, int]],
fill: Tuple[int, int, int] = (0, 0, 0),
xy: list[tuple[int, int]],
fill: tuple[int, int, int] = (0, 0, 0),
outline: int = 1,
) -> Self:
"""
@@ -564,8 +562,8 @@ class BuildImage:
@run_sync
def line(
self,
xy: Tuple[int, int, int, int],
fill: Tuple[int, int, int] | str = "#D8DEE4",
xy: tuple[int, int, int, int],
fill: tuple[int, int, int] | str = "#D8DEE4",
width: int = 1,
) -> Self:
"""
@@ -605,21 +603,19 @@ class BuildImage:
)
draw = ImageDraw.Draw(mask)
for offset, fill in (width / -2.0, "black"), (width / 2.0, "white"):
left, top = [(value + offset) * antialias for value in ellipse_box[:2]]
right, bottom = [(value - offset) * antialias for value in ellipse_box[2:]]
left, top = ((value + offset) * antialias for value in ellipse_box[:2])
right, bottom = ((value - offset) * antialias for value in ellipse_box[2:])
draw.ellipse([left, top, right, bottom], fill=fill)
mask = mask.resize(self.markImg.size, Image.LANCZOS)
try:
with contextlib.suppress(ValueError):
self.markImg.putalpha(mask)
except ValueError:
pass
return self
@run_sync
def circle_corner(
self,
radii: int = 30,
point_list: list[Literal["lt", "rt", "lb", "rb"]] = ["lt", "rt", "lb", "rb"],
point_list: list[Literal["lt", "rt", "lb", "rb"]] | None = None,
) -> Self:
"""
矩形四角变圆
@@ -631,6 +627,8 @@ class BuildImage:
返回:
BuildImage: Self
"""
if point_list is None:
point_list = ["lt", "rt", "lb", "rb"]
# 画圆(用于分离4个角)
img = self.markImg.convert("RGBA")
alpha = img.split()[-1]