|
|
@@ -0,0 +1,563 @@
|
|
|
+import os
|
|
|
+import uuid
|
|
|
+import random
|
|
|
+import math
|
|
|
+import glob
|
|
|
+import io
|
|
|
+import base64
|
|
|
+from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
+from PIL import Image, ImageDraw, ImageFont
|
|
|
+from flask import request, jsonify, send_file
|
|
|
+from lib import Qiniu
|
|
|
+import logging
|
|
|
+
|
|
|
+# 配置日志
|
|
|
+logging.basicConfig(level=logging.INFO)
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
+
|
|
|
+def get_font_list():
|
|
|
+ """
|
|
|
+ 获取"数字"文件夹下所有可用的字体文件列表
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ - 字体文件路径列表,每个元素为字典:{"path": 路径, "name": 字体名称}
|
|
|
+ """
|
|
|
+ font_dir = './手写字体'
|
|
|
+ font_list = []
|
|
|
+
|
|
|
+ if os.path.exists(font_dir):
|
|
|
+ # 查找所有字体文件(支持 .ttf, .TTF, .otf, .OTF)
|
|
|
+ font_extensions = ['*.ttf', '*.TTF', '*.otf', '*.OTF']
|
|
|
+ font_files = []
|
|
|
+ for ext in font_extensions:
|
|
|
+ font_files.extend(glob.glob(os.path.join(font_dir, ext)))
|
|
|
+
|
|
|
+ # 验证每个字体文件是否有效
|
|
|
+ for font_file in font_files:
|
|
|
+ try:
|
|
|
+ # 尝试加载字体以验证是否有效
|
|
|
+ ImageFont.truetype(font_file, 60)
|
|
|
+ # 获取字体文件名(不包含路径)
|
|
|
+ font_name = os.path.basename(font_file)
|
|
|
+ # 去掉扩展名作为显示名称
|
|
|
+ display_name = os.path.splitext(font_name)[0]
|
|
|
+ font_list.append({
|
|
|
+ "path": font_file,
|
|
|
+ "name": display_name,
|
|
|
+ "filename": font_name
|
|
|
+ })
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"跳过无效字体文件 {font_file}: {e}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 按名称排序
|
|
|
+ font_list.sort(key=lambda x: x['name'])
|
|
|
+ return font_list
|
|
|
+
|
|
|
+
|
|
|
+def find_font_file():
|
|
|
+ """
|
|
|
+ 查找可用的字体文件
|
|
|
+ 优先从"数字"文件夹随机选择一个字体,如果没有则使用系统默认字体
|
|
|
+ """
|
|
|
+ # 首先尝试从"数字"文件夹随机选择字体
|
|
|
+ font_dir = './数字'
|
|
|
+ if os.path.exists(font_dir):
|
|
|
+ # 查找所有字体文件(支持 .ttf, .TTF, .otf, .OTF)
|
|
|
+ font_extensions = ['*.ttf', '*.TTF', '*.otf', '*.OTF']
|
|
|
+ font_files = []
|
|
|
+ for ext in font_extensions:
|
|
|
+ font_files.extend(glob.glob(os.path.join(font_dir, ext)))
|
|
|
+
|
|
|
+ if font_files:
|
|
|
+ # 随机选择一个字体文件
|
|
|
+ selected_font = random.choice(font_files)
|
|
|
+ try:
|
|
|
+ # 尝试加载字体以验证是否有效
|
|
|
+ ImageFont.truetype(selected_font, 60)
|
|
|
+ logger.info(f"从数字文件夹随机选择字体: {selected_font}")
|
|
|
+ return selected_font
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"无法加载字体文件 {selected_font}: {e}")
|
|
|
+ # 如果选中的字体无效,尝试其他字体
|
|
|
+ for font_file in font_files:
|
|
|
+ try:
|
|
|
+ ImageFont.truetype(font_file, 60)
|
|
|
+ logger.info(f"使用备用字体: {font_file}")
|
|
|
+ return font_file
|
|
|
+ except:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 如果"数字"文件夹没有可用字体,尝试系统默认字体
|
|
|
+ font_paths = [
|
|
|
+ # macOS 系统字体
|
|
|
+ '/System/Library/Fonts/Supplemental/Arial.ttf',
|
|
|
+ '/System/Library/Fonts/Helvetica.ttc',
|
|
|
+ # Windows 系统字体(如果需要在Windows上运行)
|
|
|
+ 'C:/Windows/Fonts/simhei.ttf',
|
|
|
+ 'C:/Windows/Fonts/msyh.ttf',
|
|
|
+ # Linux 系统字体
|
|
|
+ '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
|
|
+ '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
|
|
|
+ ]
|
|
|
+
|
|
|
+ # 尝试查找系统字体文件
|
|
|
+ for font_path in font_paths:
|
|
|
+ if os.path.exists(font_path):
|
|
|
+ try:
|
|
|
+ return font_path
|
|
|
+ except:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 如果找不到,返回 None,将使用 PIL 默认字体
|
|
|
+ logger.warning("未找到可用字体文件,将使用 PIL 默认字体")
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def add_handwriting_effect(draw, text, x, y, font, font_size, text_color=(0, 0, 0)):
|
|
|
+ """
|
|
|
+ 添加手写效果:轻微的位置扰动
|
|
|
+ 注意:对于中文字符,建议使用手写字体文件而不是字符级别的偏移
|
|
|
+ """
|
|
|
+ chars = list(text)
|
|
|
+ current_x = x
|
|
|
+
|
|
|
+ for i, char in enumerate(chars):
|
|
|
+ # 每个字符的轻微随机偏移(模拟手写的不规则性)
|
|
|
+ offset_x = random.randint(-1, 1)
|
|
|
+ offset_y = random.randint(-1, 1)
|
|
|
+
|
|
|
+ # 绘制字符
|
|
|
+ draw.text((current_x + offset_x, y + offset_y), char, font=font, fill=text_color)
|
|
|
+
|
|
|
+ # 获取字符宽度
|
|
|
+ try:
|
|
|
+ bbox = draw.textbbox((0, 0), char, font=font)
|
|
|
+ char_width = bbox[2] - bbox[0]
|
|
|
+ except:
|
|
|
+ # 如果失败,使用简单估算
|
|
|
+ # 中文字符通常宽度约为字体大小,英文和数字约为字体大小的0.6倍
|
|
|
+ if ord(char) > 127: # 中文字符
|
|
|
+ char_width = font_size
|
|
|
+ else: # 英文或数字
|
|
|
+ char_width = int(font_size * 0.6)
|
|
|
+
|
|
|
+ current_x += char_width
|
|
|
+
|
|
|
+
|
|
|
+def generate_handwriting_image(text, font_size=60, width=None, height=None,
|
|
|
+ background_color=(255, 255, 255),
|
|
|
+ text_color=(0, 0, 0),
|
|
|
+ padding=20,
|
|
|
+ font_path=None,
|
|
|
+ use_handwriting_effect=True):
|
|
|
+ """
|
|
|
+ 生成手写字体图片
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ - text: 要生成的文字
|
|
|
+ - font_size: 字体大小(默认60)
|
|
|
+ - width: 图片宽度(None 表示自动计算)
|
|
|
+ - height: 图片高度(None 表示自动计算)
|
|
|
+ - background_color: 背景颜色(RGB元组,默认白色;如果传入RGBA元组,则支持透明度)
|
|
|
+ - text_color: 文字颜色(RGB元组,默认黑色)
|
|
|
+ - padding: 内边距(默认20像素)
|
|
|
+ - font_path: 字体文件路径(None 表示使用默认字体或自动查找)
|
|
|
+ - use_handwriting_effect: 是否使用手写效果(默认True)
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ - PIL Image 对象
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 加载字体
|
|
|
+ if font_path and os.path.exists(font_path):
|
|
|
+ try:
|
|
|
+ font = ImageFont.truetype(font_path, font_size)
|
|
|
+ except:
|
|
|
+ logger.warning(f"无法加载字体文件 {font_path},使用默认字体")
|
|
|
+ font = ImageFont.load_default()
|
|
|
+ else:
|
|
|
+ # 尝试查找字体文件
|
|
|
+ found_font_path = find_font_file()
|
|
|
+ if found_font_path:
|
|
|
+ try:
|
|
|
+ font = ImageFont.truetype(found_font_path, font_size)
|
|
|
+ except:
|
|
|
+ font = ImageFont.load_default()
|
|
|
+ else:
|
|
|
+ font = ImageFont.load_default()
|
|
|
+
|
|
|
+ # 将背景颜色转换为RGBA格式(支持透明背景)
|
|
|
+ if len(background_color) == 3:
|
|
|
+ # RGB格式,转换为RGBA,alpha设为0(完全透明)
|
|
|
+ bg_color_rgba = (*background_color, 0)
|
|
|
+ elif len(background_color) == 4:
|
|
|
+ # 已经是RGBA格式
|
|
|
+ bg_color_rgba = background_color
|
|
|
+ else:
|
|
|
+ # 默认透明背景
|
|
|
+ bg_color_rgba = (255, 255, 255, 0)
|
|
|
+
|
|
|
+ # 创建临时绘图对象来测量文字尺寸(使用RGBA模式)
|
|
|
+ temp_img = Image.new('RGBA', (100, 100), bg_color_rgba)
|
|
|
+ temp_draw = ImageDraw.Draw(temp_img)
|
|
|
+
|
|
|
+ # 获取文字边界框
|
|
|
+ try:
|
|
|
+ bbox = temp_draw.textbbox((0, 0), text, font=font)
|
|
|
+ text_width = bbox[2] - bbox[0]
|
|
|
+ text_height = bbox[3] - bbox[1]
|
|
|
+ # bbox的偏移量(某些字符可能超出基线)
|
|
|
+ bbox_offset_x = bbox[0]
|
|
|
+ bbox_offset_y = bbox[1]
|
|
|
+ except:
|
|
|
+ # 如果失败,使用简单估算
|
|
|
+ text_width = len(text) * font_size * 0.6
|
|
|
+ text_height = font_size * 1.2
|
|
|
+ bbox_offset_x = 0
|
|
|
+ bbox_offset_y = 0
|
|
|
+
|
|
|
+ # 计算图片尺寸
|
|
|
+ if width is None:
|
|
|
+ width = int(text_width + padding * 2)
|
|
|
+ if height is None:
|
|
|
+ height = int(text_height + padding * 2)
|
|
|
+
|
|
|
+ # 创建图片(使用RGBA模式支持透明背景)
|
|
|
+ image = Image.new('RGBA', (width, height), bg_color_rgba)
|
|
|
+ draw = ImageDraw.Draw(image)
|
|
|
+
|
|
|
+ # 计算文字起始位置(完美居中)
|
|
|
+ # 考虑bbox的偏移量,确保文字在图片正中央
|
|
|
+ x = (width - text_width) // 2 - bbox_offset_x
|
|
|
+ y = (height - text_height) // 2 - bbox_offset_y
|
|
|
+
|
|
|
+ # 绘制文字
|
|
|
+ if use_handwriting_effect:
|
|
|
+ # 使用手写效果(每个字符轻微偏移)
|
|
|
+ add_handwriting_effect(draw, text, x, y, font, font_size, text_color)
|
|
|
+ else:
|
|
|
+ # 直接绘制文字
|
|
|
+ draw.text((x, y), text, font=font, fill=text_color)
|
|
|
+
|
|
|
+ logger.info(f"成功生成文字图片: {text[:20]}...")
|
|
|
+ return image
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"生成文字图片失败: {e}")
|
|
|
+ raise Exception(f"生成文字图片失败: {str(e)}")
|
|
|
+
|
|
|
+
|
|
|
+def generate_and_upload_handwriting(text, font_size=60, width=None, height=None,
|
|
|
+ background_color=(255, 255, 255),
|
|
|
+ text_color=(0, 0, 0),
|
|
|
+ padding=20,
|
|
|
+ font_path=None,
|
|
|
+ use_handwriting_effect=True):
|
|
|
+ """
|
|
|
+ 生成手写字体图片并上传到七牛云
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ - text: 要生成的文字
|
|
|
+ - font_size: 字体大小(默认60)
|
|
|
+ - width: 图片宽度(None 表示自动计算)
|
|
|
+ - height: 图片高度(None 表示自动计算)
|
|
|
+ - background_color: 背景颜色(RGB元组,默认白色)
|
|
|
+ - text_color: 文字颜色(RGB元组,默认黑色)
|
|
|
+ - padding: 内边距(默认20像素)
|
|
|
+ - font_path: 字体文件路径(None 表示使用默认字体或自动查找)
|
|
|
+ - use_handwriting_effect: 是否使用手写效果(默认True)
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ - 七牛云上的图片URL
|
|
|
+ """
|
|
|
+ temp_file = None
|
|
|
+ try:
|
|
|
+ # 创建临时目录
|
|
|
+ temp_dir = "./temp"
|
|
|
+ if not os.path.exists(temp_dir):
|
|
|
+ os.makedirs(temp_dir)
|
|
|
+
|
|
|
+ # 生成图片
|
|
|
+ image = generate_handwriting_image(
|
|
|
+ text=text,
|
|
|
+ font_size=font_size,
|
|
|
+ width=width,
|
|
|
+ height=height,
|
|
|
+ background_color=background_color,
|
|
|
+ text_color=text_color,
|
|
|
+ padding=padding,
|
|
|
+ font_path=font_path,
|
|
|
+ use_handwriting_effect=use_handwriting_effect
|
|
|
+ )
|
|
|
+
|
|
|
+ # 保存临时文件
|
|
|
+ temp_file = os.path.join(temp_dir, f"handwriting_{uuid.uuid4()}.png")
|
|
|
+ image.save(temp_file, "PNG")
|
|
|
+
|
|
|
+ # 上传到七牛云
|
|
|
+ file_key = f"UpImage/{uuid.uuid4()}.png"
|
|
|
+ image_url = Qiniu.upload_to_qiniu(temp_file, file_key)
|
|
|
+
|
|
|
+ logger.info(f"成功上传文字图片到七牛云: {image_url}")
|
|
|
+ return image_url
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"生成并上传文字图片失败: {e}")
|
|
|
+ raise
|
|
|
+ finally:
|
|
|
+ # 清理临时文件
|
|
|
+ if temp_file and os.path.exists(temp_file):
|
|
|
+ try:
|
|
|
+ os.remove(temp_file)
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"清理临时文件失败: {e}")
|
|
|
+
|
|
|
+
|
|
|
+def create_handwriting_route(app):
|
|
|
+ """
|
|
|
+ 创建手写字体图片生成接口路由
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ - app: Flask应用实例
|
|
|
+ """
|
|
|
+ @app.route('/get_font_list', methods=['GET'])
|
|
|
+ def get_font_list_api():
|
|
|
+ """
|
|
|
+ 获取可用字体列表接口
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ {
|
|
|
+ "success": true,
|
|
|
+ "fonts": [
|
|
|
+ {"path": "字体路径", "name": "字体名称", "filename": "文件名"}
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ fonts = get_font_list()
|
|
|
+ return jsonify({
|
|
|
+ "success": True,
|
|
|
+ "fonts": fonts
|
|
|
+ })
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"获取字体列表失败: {e}")
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": str(e)
|
|
|
+ }), 500
|
|
|
+
|
|
|
+ def _generate_single_font_preview(font, text, font_size, width, height,
|
|
|
+ background_color, text_color, padding,
|
|
|
+ use_handwriting_effect):
|
|
|
+ """
|
|
|
+ 辅助函数:生成单个字体的预览图片
|
|
|
+ 用于多线程并行处理
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 生成图片
|
|
|
+ image = generate_handwriting_image(
|
|
|
+ text=text,
|
|
|
+ font_size=font_size,
|
|
|
+ width=width,
|
|
|
+ height=height,
|
|
|
+ background_color=background_color,
|
|
|
+ text_color=text_color,
|
|
|
+ padding=padding,
|
|
|
+ font_path=font['path'],
|
|
|
+ use_handwriting_effect=use_handwriting_effect
|
|
|
+ )
|
|
|
+
|
|
|
+ # 将图片转换为base64
|
|
|
+ img_io = io.BytesIO()
|
|
|
+ image.save(img_io, 'PNG')
|
|
|
+ img_io.seek(0)
|
|
|
+ img_base64 = base64.b64encode(img_io.read()).decode('utf-8')
|
|
|
+
|
|
|
+ return {
|
|
|
+ "font_path": font['path'],
|
|
|
+ "font_name": font['name'],
|
|
|
+ "image_base64": "data:image/png;base64," + img_base64
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"生成字体 {font['name']} 预览失败: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+ @app.route('/batch_generate_preview', methods=['POST'])
|
|
|
+ def batch_generate_preview():
|
|
|
+ """
|
|
|
+ 批量生成字体预览接口(返回多个图片的base64编码)
|
|
|
+
|
|
|
+ 请求参数(JSON):
|
|
|
+ {
|
|
|
+ "text": "要预览的文字",
|
|
|
+ "font_size": 60,
|
|
|
+ "padding": 20,
|
|
|
+ "background_color": [255, 255, 255, 0],
|
|
|
+ "text_color": [0, 0, 0],
|
|
|
+ "use_handwriting_effect": true
|
|
|
+ }
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ {
|
|
|
+ "success": true,
|
|
|
+ "previews": [
|
|
|
+ {
|
|
|
+ "font_path": "字体路径",
|
|
|
+ "font_name": "字体名称",
|
|
|
+ "image_base64": "base64编码的图片数据"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ data = request.get_json()
|
|
|
+ if not data:
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": "请求参数为空"
|
|
|
+ }), 400
|
|
|
+
|
|
|
+ text = data.get('text')
|
|
|
+ if not text:
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": "缺少text参数"
|
|
|
+ }), 400
|
|
|
+
|
|
|
+ # 获取可选参数
|
|
|
+ font_size = data.get('font_size', 60)
|
|
|
+ width = data.get('width')
|
|
|
+ height = data.get('height')
|
|
|
+ background_color = tuple(data.get('background_color', [255, 255, 255]))
|
|
|
+ text_color = tuple(data.get('text_color', [0, 0, 0]))
|
|
|
+ padding = data.get('padding', 20)
|
|
|
+ use_handwriting_effect = data.get('use_handwriting_effect', True)
|
|
|
+
|
|
|
+ # 获取所有字体
|
|
|
+ fonts = get_font_list()
|
|
|
+ if not fonts:
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": "没有可用的字体"
|
|
|
+ }), 400
|
|
|
+
|
|
|
+ previews = []
|
|
|
+
|
|
|
+ # 使用线程池并行处理,最多同时处理10个字体(可根据需要调整)
|
|
|
+ max_workers = min(10, len(fonts)) # 根据字体数量动态调整线程数
|
|
|
+
|
|
|
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
|
+ # 提交所有任务
|
|
|
+ future_to_font = {
|
|
|
+ executor.submit(
|
|
|
+ _generate_single_font_preview,
|
|
|
+ font, text, font_size, width, height,
|
|
|
+ background_color, text_color, padding,
|
|
|
+ use_handwriting_effect
|
|
|
+ ): font for font in fonts
|
|
|
+ }
|
|
|
+
|
|
|
+ # 收集结果
|
|
|
+ for future in as_completed(future_to_font):
|
|
|
+ result = future.result()
|
|
|
+ if result is not None:
|
|
|
+ previews.append(result)
|
|
|
+
|
|
|
+ return jsonify({
|
|
|
+ "success": True,
|
|
|
+ "previews": previews,
|
|
|
+ "total": len(previews)
|
|
|
+ })
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"批量生成预览失败: {e}")
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": str(e)
|
|
|
+ }), 500
|
|
|
+
|
|
|
+ @app.route('/generate_handwriting', methods=['POST'])
|
|
|
+ def generate_handwriting():
|
|
|
+ """
|
|
|
+ 生成手写字体图片接口(直接返回图片文件)
|
|
|
+
|
|
|
+ 请求参数(JSON):
|
|
|
+ {
|
|
|
+ "text": "要生成的文字", // 必需
|
|
|
+ "font_size": 60, // 可选,字体大小(默认60)
|
|
|
+ "width": null, // 可选,图片宽度(null表示自动计算)
|
|
|
+ "height": null, // 可选,图片高度(null表示自动计算)
|
|
|
+ "background_color": [255, 255, 255] 或 [255, 255, 255, 0], // 可选,背景颜色RGB或RGBA(默认透明背景,alpha=0)
|
|
|
+ "text_color": [0, 0, 0], // 可选,文字颜色RGB(默认黑色)
|
|
|
+ "padding": 20, // 可选,内边距(默认20像素)
|
|
|
+ "font_path": null, // 可选,字体文件路径(null表示使用默认字体)
|
|
|
+ "use_handwriting_effect": true // 可选,是否使用手写效果(默认true)
|
|
|
+ }
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ - PNG图片文件(直接返回,不返回JSON)
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 获取请求参数
|
|
|
+ data = request.get_json()
|
|
|
+
|
|
|
+ if not data:
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": "请求参数为空"
|
|
|
+ }), 400
|
|
|
+
|
|
|
+ text = data.get('text')
|
|
|
+ if not text:
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": "缺少text参数"
|
|
|
+ }), 400
|
|
|
+
|
|
|
+ # 获取可选参数
|
|
|
+ font_size = data.get('font_size', 60)
|
|
|
+ width = data.get('width')
|
|
|
+ height = data.get('height')
|
|
|
+ background_color = tuple(data.get('background_color', [255, 255, 255]))
|
|
|
+ text_color = tuple(data.get('text_color', [0, 0, 0]))
|
|
|
+ padding = data.get('padding', 20)
|
|
|
+ font_path = data.get('font_path')
|
|
|
+ use_handwriting_effect = data.get('use_handwriting_effect', True)
|
|
|
+
|
|
|
+ # 生成图片
|
|
|
+ image = generate_handwriting_image(
|
|
|
+ text=text,
|
|
|
+ font_size=font_size,
|
|
|
+ width=width,
|
|
|
+ height=height,
|
|
|
+ background_color=background_color,
|
|
|
+ text_color=text_color,
|
|
|
+ padding=padding,
|
|
|
+ font_path=font_path,
|
|
|
+ use_handwriting_effect=use_handwriting_effect
|
|
|
+ )
|
|
|
+
|
|
|
+ # 将图片保存到内存中的字节流
|
|
|
+ img_io = io.BytesIO()
|
|
|
+ image.save(img_io, 'PNG')
|
|
|
+ img_io.seek(0)
|
|
|
+
|
|
|
+ logger.info(f"成功生成文字图片: {text[:20]}...")
|
|
|
+
|
|
|
+ # 直接返回图片文件
|
|
|
+ return send_file(
|
|
|
+ img_io,
|
|
|
+ mimetype='image/png',
|
|
|
+ as_attachment=False,
|
|
|
+ download_name='handwriting.png'
|
|
|
+ )
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"接口处理失败: {e}")
|
|
|
+ return jsonify({
|
|
|
+ "success": False,
|
|
|
+ "error": str(e)
|
|
|
+ }), 500
|
|
|
+
|