放大图形验证码文字

This commit is contained in:
Certificate System
2026-08-13 15:46:50 +08:00
parent f72645225a
commit eac19ea593
3 changed files with 51 additions and 9 deletions

View File

@@ -4,10 +4,19 @@ import random
import secrets
import string
import time
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
CAPTCHA_TTL_SECONDS = 300
CAPTCHA_WIDTH = 148
CAPTCHA_HEIGHT = 48
CAPTCHA_FONT_SIZE = 30
CAPTCHA_FONT_PATHS = (
Path("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"),
Path("/System/Library/Fonts/SFNS.ttf"),
Path("/Library/Fonts/Arial Bold.ttf"),
)
_captchas: dict[str, tuple[str, float]] = {}
@@ -38,17 +47,29 @@ def clean_expired_captchas() -> None:
def draw_captcha_image(code: str) -> str:
width, height = 132, 44
image = Image.new("RGB", (width, height), "#f8fafc")
image = Image.new("RGB", (CAPTCHA_WIDTH, CAPTCHA_HEIGHT), "#f8fafc")
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
font = load_captcha_font()
for i, char in enumerate(code):
draw.text((18 + i * 26, 13 + random.randint(-2, 2)), char, fill="#1f2937", font=font)
bounds = draw.textbbox((0, 0), char, font=font)
text_height = bounds[3] - bounds[1]
y = (CAPTCHA_HEIGHT - text_height) // 2 - bounds[1] + random.randint(-2, 2)
draw.text((12 + i * 33, y), char, fill="#111827", font=font)
for _ in range(8):
x1, y1 = random.randint(0, width), random.randint(0, height)
x2, y2 = random.randint(0, width), random.randint(0, height)
x1, y1 = random.randint(0, CAPTCHA_WIDTH), random.randint(0, CAPTCHA_HEIGHT)
x2, y2 = random.randint(0, CAPTCHA_WIDTH), random.randint(0, CAPTCHA_HEIGHT)
draw.line((x1, y1, x2, y2), fill="#cbd5e1", width=1)
buffer = io.BytesIO()
image.save(buffer, format="PNG")
return "data:image/png;base64," + base64.b64encode(buffer.getvalue()).decode("ascii")
def load_captcha_font() -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
for path in CAPTCHA_FONT_PATHS:
if path.exists():
return ImageFont.truetype(str(path), CAPTCHA_FONT_SIZE)
try:
return ImageFont.load_default(size=CAPTCHA_FONT_SIZE)
except TypeError:
return ImageFont.load_default()