用python脚本加html自建的书法字典

目录

一、前言

本文介绍一种自制书法字典的方法,它可以将我们输入的任意内容,转换成我们需要的书法作品。它不依赖网络,完全在本地运行,只要你导入想要的书法图片库,就可以了。如下图中,我们输入了"厚德载物",前台就会转换成相应的书法字体。

下面我们从头说这个软件工具在windows下的搭建步骤:

第一步、安装python

  • 本方法需要先在电脑上安装python环境。具体安装配置方法就不说了。确保你的电脑已安装 Python(版本 3.6 以上)。

第二步、导入书法图片库

  • 先创建一个主目录,然后在主目录下创建一个文件夹,用来存放你的书法图片,图片格式为png。如本文中,书法图片库的文件夹名称为calligraphy_images。
  • 然后将你的书法图片存放到calligraphy_images文件夹下,这些图片的制作方法,我在前面的文章中有介绍过,可以参考:
  • 注意这些图片的名称要和书法字的实际内容对应。

第三步:编写 Python 脚本生成 index.json

  • 创建一个 Python 文件,用来生成字体与实际文件名的映射关系,以便在你导入新的字库时,自动加入到系统。例如本文中的 generate_index.py,脚本与 calligraphy_images文件夹放在同一级目录下。

这个脚本内容如下:

c 复制代码
import os
import json
import glob

def generate_image_index(image_folder, output_json="index.json", base_url="./calligraphy_images/"):
    """
    扫描 image_folder 下的所有图片文件,生成 汉字 -> 图片路径 的映射 JSON。
    :param image_folder: 图片文件夹路径
    :param output_json: 输出的 JSON 文件名
    :param base_url: 前端访问图片的基础 URL(相对路径或绝对路径)
    """
    # 支持的图片扩展名
    extensions = ('*.png', '*.jpg', '*.jpeg', '*.bmp', '*.tiff')
    image_files = []
    for ext in extensions:
        image_files.extend(glob.glob(os.path.join(image_folder, ext)))
        image_files.extend(glob.glob(os.path.join(image_folder, ext.upper())))
    
    # 去重并排序
    image_files = sorted(set(image_files))
    
    mapping = {}
    for full_path in image_files:
        # 提取不带扩展名的文件名作为汉字(假设文件名就是汉字字符)
        basename = os.path.basename(full_path)
        # 移除扩展名
        char_name = os.path.splitext(basename)[0]
        # 构建前端可访问的路径(使用正斜杠,兼容 Windows)
        relative_path = os.path.join(base_url, basename).replace('\\', '/')
        mapping[char_name] = relative_path
    
    # 写入 JSON 文件
    with open(output_json, 'w', encoding='utf-8') as f:
        json.dump(mapping, f, ensure_ascii=False, indent=2)
    
    print(f"成功生成 {output_json},共包含 {len(mapping)} 个汉字映射。")

if __name__ == "__main__":
    # 假设图片文件夹名为 calligraphy_images,与脚本同级
    generate_image_index("./calligraphy_images", "index.json", "./calligraphy_images/")

第四步:编写前端代码

  • 编写前端html页面, 如本文中的calligraphy_generator.html,放在与 calligraphy_images相同的目录下。
  • 前端文件calligraphy_generator.html的内容如下:
c 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>书法字库 · 自动映射生成器</title>
    <style>
        * {
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, 'Microsoft YaHei', sans-serif;
            max-width: 1200px;
            margin: 2rem auto;
            padding: 20px;
            background: #f5f3ef;
            color: #2c2c2c;
        }
        
        .container {
            background: white;
            border-radius: 24px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.08);
            padding: 2rem;
        }
        
        h1 {
            margin-top: 0;
            color: #8b5a2b;
            border-left: 5px solid #d4a373;
            padding-left: 20px;
            font-weight: 500;
        }
        
        .input-area {
            margin: 2rem 0;
        }
        
        label {
            display: block;
            font-weight: 600;
            margin-bottom: 8px;
            color: #5a3e2b;
        }
        
        textarea {
            width: 100%;
            padding: 12px;
            font-size: 1.1rem;
            border: 1px solid #ddd;
            border-radius: 12px;
            font-family: inherit;
            resize: vertical;
            background: #fefaf5;
        }
        
        textarea:focus {
            outline: none;
            border-color: #d4a373;
            box-shadow: 0 0 0 3px rgba(212, 163, 115, 0.2);
        }
        
        button {
            background: #d4a373;
            color: white;
            border: none;
            padding: 10px 24px;
            font-size: 1rem;
            font-weight: 600;
            border-radius: 40px;
            cursor: pointer;
            transition: all 0.2s ease;
            margin-top: 12px;
        }
        
        button:hover {
            background: #b8865b;
            transform: translateY(-1px);
        }
        
        button:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }
        
        .result-area {
            margin: 2rem 0;
            padding: 1.5rem;
            background: #fefaf5;
            border-radius: 20px;
            border: 1px solid #f0e2d4;
            text-align: center;
        }
        
        .result-header {
            font-weight: 600;
            margin-bottom: 16px;
            color: #7a5c3a;
            text-align: left;
        }
        
        #outputCanvas {
            max-width: 100%;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            background: white;
        }
        
        .status {
            margin-top: 16px;
            font-size: 0.9rem;
            color: #b5835a;
        }
        
        .footer {
            font-size: 0.75rem;
            text-align: center;
            margin-top: 2rem;
            color: #b6a28b;
            border-top: 1px solid #efe2d6;
            padding-top: 1.5rem;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>🖌️ 书法字库 · 自动映射生成器</h1>
    <p>输入任意句子,系统将自动从本地书法字库中匹配对应单字图片,并按顺序拼接成完整书法作品。</p>
    
    <div class="input-area">
        <label>📝 输入句子:</label>
        <textarea id="sentenceInput" rows="3" placeholder="例如:上善若水 厚德载物" style="font-size: 1.2rem;">厚德载物</textarea>
        <button id="generateBtn" disabled>⏳ 字库加载中...</button>
    </div>
    
    <div class="result-area">
        <div class="result-header">🖼️ 生成结果:</div>
        <canvas id="outputCanvas" width="800" height="200" style="max-width:100%; height:auto; background:#fef6e8;"></canvas>
        <div id="statusMsg" class="status"></div>
    </div>
    <div class="footer">
        ⚙️ 说明:字库映射自动从 index.json 加载。请确保通过 HTTP 服务器访问本页面(例如 <code>python -m http.server 8000</code>)。
    </div>
</div>

<script>
    // 配置参数
    const GAP_BETWEEN_CHARS = 5;   // 字间距(像素)
    const MAPPING_JSON_URL = 'index.json';   // 映射文件路径

    let charImageMap = {};          // 存储 汉字 -> 图片路径
    let mappingLoaded = false;       // 映射是否加载完成
    const generateBtn = document.getElementById('generateBtn');
    const sentenceInput = document.getElementById('sentenceInput');
    const outputCanvas = document.getElementById('outputCanvas');
    const statusMsgDiv = document.getElementById('statusMsg');

    // 加载映射表
    async function loadMapping() {
        statusMsgDiv.innerHTML = '📚 正在加载字库映射表...';
        try {
            const response = await fetch(MAPPING_JSON_URL);
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            charImageMap = await response.json();
            const count = Object.keys(charImageMap).length;
            statusMsgDiv.innerHTML = `✅ 字库加载成功!共 ${count} 个书法单字。`;
            mappingLoaded = true;
            generateBtn.disabled = false;
            generateBtn.textContent = '✨ 生成书法句子';
        } catch (err) {
            console.error('加载映射失败:', err);
            statusMsgDiv.innerHTML = `❌ 无法加载字库映射文件 (${MAPPING_JSON_URL})。请确保:<br>
                1. 已运行 Python 脚本生成 index.json;<br>
                2. 通过 HTTP 服务器访问本页面(如 python -m http.server 8000)。<br>
                错误详情:${err.message}`;
            generateBtn.disabled = true;
            generateBtn.textContent = '❌ 字库加载失败';
        }
    }

    // 异步加载单张图片
    function loadImage(src) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
            img.src = src;
        });
    }

    // 拼接句子
    async function composeSentence(sentence) {
        if (!mappingLoaded) {
            statusMsgDiv.innerHTML = '⏳ 字库尚未加载完成,请稍后再试。';
            return;
        }

        const chars = sentence.split('');
        const tasks = [];
        const missingChars = [];

        for (let char of chars) {
            if (char.trim() === '') continue;
            const imgPath = charImageMap[char];
            if (!imgPath) {
                missingChars.push(char);
                tasks.push(null);
            } else {
                tasks.push(loadImage(imgPath));
            }
        }

        if (missingChars.length > 0) {
            statusMsgDiv.innerHTML = `⚠️ 警告:以下汉字在字库中不存在:${missingChars.join('、')}<br>将只生成存在的部分。`;
        } else {
            statusMsgDiv.innerHTML = '✅ 所有汉字均在字库中找到,正在拼接...';
        }

        const results = await Promise.allSettled(tasks.map(task => task ? task : Promise.reject('图片路径不存在')));
        const validImages = [];
        let idx = 0;
        for (let char of chars) {
            if (char.trim() === '') continue;
            const result = results[idx];
            if (result.status === 'fulfilled') {
                validImages.push({
                    char: char,
                    img: result.value,
                    width: result.value.width,
                    height: result.value.height
                });
            }
            idx++;
        }

        if (validImages.length === 0) {
            statusMsgDiv.innerHTML = '❌ 没有成功加载任何图片,请检查图片文件是否存在。';
            return;
        }

        // 计算 canvas 尺寸
        let maxHeight = 0;
        let totalWidth = 0;
        for (let item of validImages) {
            maxHeight = Math.max(maxHeight, item.height);
            totalWidth += item.width;
        }
        totalWidth += (validImages.length - 1) * GAP_BETWEEN_CHARS;

        outputCanvas.width = totalWidth;
        outputCanvas.height = maxHeight;

        const ctx = outputCanvas.getContext('2d');
        ctx.fillStyle = '#fef6e8';
        ctx.fillRect(0, 0, totalWidth, maxHeight);

        let currentX = 0;
        for (let item of validImages) {
            const yOffset = (maxHeight - item.height) / 2;
            ctx.drawImage(item.img, currentX, yOffset, item.width, item.height);
            currentX += item.width + GAP_BETWEEN_CHARS;
        }
        statusMsgDiv.innerHTML = `✨ 生成成功!共合成 ${validImages.length} 个书法单字。`;
    }

    // 事件绑定
    generateBtn.addEventListener('click', async () => {
        const sentence = sentenceInput.value.trim();
        if (!sentence) {
            statusMsgDiv.innerHTML = '⚠️ 请输入一句需要生成的文字。';
            return;
        }
        statusMsgDiv.innerHTML = '🖌️ 正在合成,请稍候...';
        await composeSentence(sentence);
    });

    // 启动时加载映射表
    loadMapping();
</script>
</body>
</html>

第五步、运行脚本,生成index.json文件

  • 打开终端(CMD / PowerShell / Terminal),进入该目录,执行:

    c 复制代码
    python generate_index.py
  • 运行成功后,会在同一目录下自动生成 index.json 文件:

index.json文件的内容类似:

c 复制代码
{
   "一": "./calligraphy_images/一.png",
  "丁": "./calligraphy_images/丁.png",
  "七": "./calligraphy_images/七.png",
  "万": "./calligraphy_images/万.png",
  "丈": "./calligraphy_images/丈.png",
  "三": "./calligraphy_images/三.png",
  "上": "./calligraphy_images/上.png",
  "下": "./calligraphy_images/下.png",
  "不": "./calligraphy_images/不.png",
  "与": "./calligraphy_images/与.png",
  "丐": "./calligraphy_images/丐.png",
  "丑": "./calligraphy_images/丑.png"
}

第六步:启动 Python 自带的 HTTP 服务器

  • 打开终端,进入包含 calligraphy_generator.html、index.json 和 calligraphy_images文件夹的目录。执行以下命令:

    c 复制代码
    python -m http.server 8000

第六步:访问本地书法字典

  • 在浏览器中输入以下网址,就可以打开字典了。
c 复制代码
 http://localhost:8000/calligraphy_generator.html
  • 打开页面的时候,下面会显示加载成功的字库数量:
  • 现在,你就可以任意使用了,想让它输出什么都行,当然,你的图片库中要有东西
相关推荐
The moon forgets1 小时前
DreamVLA:世界知识驱动的视觉-语言-动作新范式
人工智能·pytorch·python·深度学习·具身智能·vla
凯瑟琳.奥古斯特1 小时前
力扣1003题C++解法详解
开发语言·c++·算法·leetcode·职场和发展
myenjoy_11 小时前
Python + Snap7 实现西门子 S7-1200/1500 数据采集
开发语言·python
大学竞赛君1 小时前
第十六届蓝桥杯大赛软件赛决赛 Python 大学 A 组
python·职场和发展·蓝桥杯
c238561 小时前
C++11final与override6、智能指针
开发语言·c++
-FxYaM-1 小时前
图吧工具箱与自动化运维
python
aqi001 小时前
15天学会AI应用开发(四)根据Token长度截断历史对话
人工智能·python·大模型·ai编程·ai应用
*neverGiveUp*1 小时前
初步了解Django框架
开发语言·python·django
Java_2017_csdn1 小时前
在 Java 中,MessageFormat.format() 和 String.format() 函数对比?
java·开发语言·前端·数据库