refresh gitignore

This commit is contained in:
douboer
2026-02-11 17:17:24 +08:00
parent 4c7cbc8ae2
commit 2329d36260
22 changed files with 590 additions and 2837 deletions

2
.gitignore vendored
View File

@@ -24,7 +24,9 @@ node_modules
*.ttf *.ttf
frontend/vite.config.ts frontend/vite.config.ts
frontend/dist/fonts.json
frontend/public/fonts.json frontend/public/fonts.json
miniprogram/assets/fonts.json
# secrets # secrets
.env .env

Binary file not shown.

View File

@@ -1 +0,0 @@
var u=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function a(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function f(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var n=e.default;if(typeof n=="function"){var t=function r(){var o=!1;try{o=this instanceof r}catch{}return o?Reflect.construct(n,arguments,this.constructor):n.apply(this,arguments)};t.prototype=n.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}),t}export{a,u as c,f as g};

View File

@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/jszip.min-CRv3yS3R.js","assets/_commonjsHelpers-DsqdWQfm.js"])))=>i.map(i=>d[i]);
import{_ as v}from"./index-CKp66E8b.js";function p(t,e,n="text/plain"){const o=new Blob([t],{type:n});s(o,e)}function s(t,e){const n=URL.createObjectURL(t),o=document.createElement("a");o.href=n,o.download=e,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function P(t,e){p(t,e,"image/svg+xml")}async function F(t,e="font2svg-export.zip"){const n=(await v(async()=>{const{default:r}=await import("./jszip.min-CRv3yS3R.js").then(i=>i.j);return{default:r}},__vite__mapDeps([0,1]))).default,o=new n;for(const r of t)o.file(r.name,r.content);const a=await o.generateAsync({type:"blob"});s(a,e)}function d(t){if(!t)return null;const e=t.match(/-?\d+(\.\d+)?/);if(!e)return null;const n=Number(e[0]);return Number.isFinite(n)?n:null}function x(t){const n=new DOMParser().parseFromString(t,"image/svg+xml").documentElement,o=d(n.getAttribute("width")),a=d(n.getAttribute("height"));if(o&&a)return{width:o,height:a};const r=n.getAttribute("viewBox");if(r){const i=r.trim().split(/[\s,]+/).map(Number);if(i.length===4&&Number.isFinite(i[2])&&Number.isFinite(i[3]))return{width:Math.max(1,i[2]),height:Math.max(1,i[3])}}return{width:1024,height:1024}}async function _(t,e){const n=x(t),o=e?.scale??1,a=Math.max(1,Math.round((e?.width??n.width)*o)),r=Math.max(1,Math.round((e?.height??n.height)*o)),i=document.createElement("canvas");i.width=a,i.height=r;const l=i.getContext("2d");if(!l)throw new Error("无法创建 PNG 画布");e?.backgroundColor?(l.fillStyle=e.backgroundColor,l.fillRect(0,0,a,r)):l.clearRect(0,0,a,r);const f=new Blob([t],{type:"image/svg+xml;charset=utf-8"}),u=URL.createObjectURL(f);try{const c=new Image;await new Promise((w,b)=>{c.onload=()=>w(),c.onerror=()=>b(new Error("SVG 转 PNG 失败")),c.src=u}),l.drawImage(c,0,0,a,r)}finally{URL.revokeObjectURL(u)}const g=await new Promise(c=>{i.toBlob(c,"image/png")});if(!g)throw new Error("PNG 编码失败");return g}async function R(t,e,n){const o=await _(t,n);s(o,e)}function m(t){return t.replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g,"_").substring(0,200)}function h(t,e){const n=m(Array.from(t).slice(0,8).join(""));return`${m(e.substring(0,20))}_${n}`}function B(t,e){return`${h(t,e)}.svg`}function L(t,e){return`${h(t,e)}.png`}export{_ as convertSvgToPngBlob,s as downloadBlob,F as downloadMultipleFiles,R as downloadPngFromSvg,P as downloadSvg,p as downloadText,L as generatePngFilename,B as generateSvgFilename,m as sanitizeFilename};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 170 KiB

View File

@@ -1,11 +0,0 @@
{
"inputText": "星程字体转换",
"fontSize": 50,
"textColor": "#dc2626",
"selectedFontIds": [
"0001"
],
"favoriteFontIds": [
"0001"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,170 +0,0 @@
[
{
"id": "0001",
"name": "AlimamaDaoLiTi",
"filename": "AlimamaDaoLiTi.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/AlimamaDaoLiTi.ttf"
},
{
"id": "0002",
"name": "Hangeuljaemin4-Regular",
"filename": "Hangeuljaemin4-Regular.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/Hangeuljaemin4-Regular.ttf"
},
{
"id": "0003",
"name": "I.顏體",
"filename": "I.顏體.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/I.顏體.ttf"
},
{
"id": "0004",
"name": "XCDUANZHUANGSONGTI",
"filename": "XCDUANZHUANGSONGTI.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/XCDUANZHUANGSONGTI.ttf"
},
{
"id": "0005",
"name": "qiji-combo",
"filename": "qiji-combo.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/qiji-combo.ttf"
},
{
"id": "0006",
"name": "临海隶书",
"filename": "临海隶书.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/临海隶书.ttf"
},
{
"id": "0007",
"name": "京華老宋体_KingHwa_OldSong",
"filename": "京華老宋体_KingHwa_OldSong.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/京華老宋体_KingHwa_OldSong.ttf"
},
{
"id": "0008",
"name": "优设标题黑",
"filename": "优设标题黑.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/优设标题黑.ttf"
},
{
"id": "0009",
"name": "包图小白体",
"filename": "包图小白体.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/包图小白体.ttf"
},
{
"id": "0010",
"name": "源界明朝",
"filename": "源界明朝.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/源界明朝.ttf"
},
{
"id": "0011",
"name": "演示佛系体",
"filename": "演示佛系体.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/演示佛系体.ttf"
},
{
"id": "0012",
"name": "站酷快乐体",
"filename": "站酷快乐体.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/站酷快乐体.ttf"
},
{
"id": "0013",
"name": "问藏书房",
"filename": "问藏书房.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/问藏书房.ttf"
},
{
"id": "0014",
"name": "霞鹜臻楷",
"filename": "霞鹜臻楷.ttf",
"category": "其他字体",
"path": "/fonts/其他字体/霞鹜臻楷.ttf"
},
{
"id": "0015",
"name": "庞门正道标题体",
"filename": "庞门正道标题体.ttf",
"category": "庞门正道",
"path": "/fonts/庞门正道/庞门正道标题体.ttf"
},
{
"id": "0016",
"name": "庞门正道标题体",
"filename": "庞门正道标题体.ttf",
"category": "庞门正道-测试",
"path": "/fonts/庞门正道-测试/庞门正道标题体.ttf"
},
{
"id": "0017",
"name": "王漢宗勘亭流繁",
"filename": "王漢宗勘亭流繁.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗勘亭流繁.ttf"
},
{
"id": "0018",
"name": "王漢宗新潮體",
"filename": "王漢宗新潮體.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗新潮體.ttf"
},
{
"id": "0019",
"name": "王漢宗波卡體空陰",
"filename": "王漢宗波卡體空陰.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗波卡體空陰.ttf"
},
{
"id": "0020",
"name": "王漢宗細黑體繁",
"filename": "王漢宗細黑體繁.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗細黑體繁.ttf"
},
{
"id": "0021",
"name": "王漢宗綜藝體雙空陰",
"filename": "王漢宗綜藝體雙空陰.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗綜藝體雙空陰.ttf"
},
{
"id": "0022",
"name": "王漢宗超明體繁",
"filename": "王漢宗超明體繁.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗超明體繁.ttf"
},
{
"id": "0023",
"name": "王漢宗酷儷海報",
"filename": "王漢宗酷儷海報.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗酷儷海報.ttf"
},
{
"id": "0024",
"name": "王漢宗顏楷體繁",
"filename": "王漢宗顏楷體繁.ttf",
"category": "王漢宗",
"path": "/fonts/王漢宗/王漢宗顏楷體繁.ttf"
}
]

View File

@@ -1,17 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<!-- 统一使用 favicon_new.png 作为站点图标,避免 Safari 继续优先采用 SVG 或 mask 图标 -->
<link rel="icon" type="image/png" href="/favicon_new.png" />
<link rel="shortcut icon" href="/favicon_new.png" />
<link rel="apple-touch-icon" href="/favicon_new.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Font2SVG - 字体转SVG工具</title>
<script type="module" crossorigin src="/assets/index-CKp66E8b.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DQpT4mud.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

File diff suppressed because it is too large Load Diff

376
scripts/font-id-state.json Normal file
View File

@@ -0,0 +1,376 @@
{
"version": 1,
"nextId": 370,
"pathToId": {
"上首造字/No.1-上首锐博体.ttf": "0001",
"上首造字/No.10-上首叮当体.ttf": "0002",
"上首造字/No.100-上首纤宋体.ttf": "0003",
"上首造字/No.101-上首武士黑体.ttf": "0004",
"上首造字/No.102-上首玄藏体.ttf": "0005",
"上首造字/No.103-上首星际体.ttf": "0006",
"上首造字/No.104-上首鸿志手写体.ttf": "0007",
"上首造字/No.105-上首逍遥体.ttf": "0008",
"上首造字/No.106-上首简墨体.ttf": "0009",
"上首造字/No.107-上首松羽体.ttf": "0010",
"上首造字/No.108-上首燕尾体.ttf": "0011",
"上首造字/No.109-上首韶华体.ttf": "0012",
"上首造字/No.11-上首车车体.ttf": "0013",
"上首造字/No.110-上首烈宋体.ttf": "0014",
"上首造字/No.111-上首方舟体.ttf": "0015",
"上首造字/No.112-上首壹方体.ttf": "0016",
"上首造字/No.113-上首轩正体.ttf": "0017",
"上首造字/No.114-上首长城体.ttf": "0018",
"上首造字/No.115-上首音符体.ttf": "0019",
"上首造字/No.116-上首如意体.ttf": "0020",
"上首造字/No.117-上首酷峰体.ttf": "0021",
"上首造字/No.118-上首星耀体.ttf": "0022",
"上首造字/No.119-上首罗马体.ttf": "0023",
"上首造字/No.12-上首秀圆体.ttf": "0024",
"上首造字/No.120-上首宽言体.ttf": "0025",
"上首造字/No.121-上首刻宋体.ttf": "0026",
"上首造字/No.122-上首云润体.ttf": "0027",
"上首造字/No.123-上首方义手刷体.ttf": "0028",
"上首造字/No.124-上首艺圆体.ttf": "0029",
"上首造字/No.125-上首方尊体.ttf": "0030",
"上首造字/No.126-上首吉言体.ttf": "0031",
"上首造字/No.127-上首简一体.ttf": "0032",
"上首造字/No.128-上首玄黑体.ttf": "0033",
"上首造字/No.129-上首墨遥体.ttf": "0034",
"上首造字/No.13-上首华凤书法体.ttf": "0035",
"上首造字/No.130-上首追光手写体.ttf": "0036",
"上首造字/No.131-上首斗金体.ttf": "0037",
"上首造字/No.132-上首豆干体.ttf": "0038",
"上首造字/No.133-上首魔法体.ttf": "0039",
"上首造字/No.134-上首文化体.ttf": "0040",
"上首造字/No.135-上首龙吟体.ttf": "0041",
"上首造字/No.136-上首悠然体.ttf": "0042",
"上首造字/No.137-上首本草纲目体.ttf": "0043",
"上首造字/No.138-上首星斗体.ttf": "0044",
"上首造字/No.139-上首鼎宋体.ttf": "0045",
"上首造字/No.14-上首水滴体.ttf": "0046",
"上首造字/No.140-上首妖刀体.ttf": "0047",
"上首造字/No.141-上首高达体.ttf": "0048",
"上首造字/No.142-上首综艺体.ttf": "0049",
"上首造字/No.143-上首圆木体.ttf": "0050",
"上首造字/No.144-上首逍遥书法体.ttf": "0051",
"上首造字/No.145-上首高光体.ttf": "0052",
"上首造字/No.146-上首清雅宋体.ttf": "0053",
"上首造字/No.147-上首云隶体.ttf": "0054",
"上首造字/No.148-上首标榜体.ttf": "0055",
"上首造字/No.149-上首奶酪体.ttf": "0056",
"上首造字/No.15-上首润黑体.ttf": "0057",
"上首造字/No.150-上首秋刀体.ttf": "0058",
"上首造字/No.151-上首凤鸣体.ttf": "0059",
"上首造字/No.152-上首南枝体.ttf": "0060",
"上首造字/No.152上首南枝体.ttf": "0061",
"上首造字/No.153-上首可乐体.ttf": "0062",
"上首造字/No.154-上首长青体.ttf": "0063",
"上首造字/No.155-上首中正宋体.ttf": "0064",
"上首造字/No.156-上首曲奇体.ttf": "0065",
"上首造字/No.157-上首萌萌手写体.ttf": "0066",
"上首造字/No.158-上首朗润体.ttf": "0067",
"上首造字/No.159-上首财神体.ttf": "0068",
"上首造字/No.16-上首锐圆体.ttf": "0069",
"上首造字/No.160-上首混沌体.ttf": "0070",
"上首造字/No.161-上首刺云体.ttf": "0071",
"上首造字/No.162-上首幻视体.ttf": "0072",
"上首造字/No.163-上首万象体.ttf": "0073",
"上首造字/No.164-上首简正体.ttf": "0074",
"上首造字/No.165-上首刺秦体.ttf": "0075",
"上首造字/No.166-上首暗黑体.ttf": "0076",
"上首造字/No.167-上首萌虎体.ttf": "0077",
"上首造字/No.168-上首雪糕体.ttf": "0078",
"上首造字/No.169-上首逐浪书法体.ttf": "0079",
"上首造字/No.17-上首钝黑体.ttf": "0080",
"上首造字/No.170-上首方玉体.ttf": "0081",
"上首造字/No.171-上首龙猫体.ttf": "0082",
"上首造字/No.172-上首木兰体.ttf": "0083",
"上首造字/No.173-上首山水宋体.ttf": "0084",
"上首造字/No.174-上首扑克体.ttf": "0085",
"上首造字/No.175-上首潮玩体.ttf": "0086",
"上首造字/No.176-上首鲁班宋体.ttf": "0087",
"上首造字/No.177-上首奶糖体.ttf": "0088",
"上首造字/No.178-上首浓墨体.ttf": "0089",
"上首造字/No.179-上首朋克体.ttf": "0090",
"上首造字/No.18-上首疾风书法体.ttf": "0091",
"上首造字/No.180-上首风云书法体.ttf": "0092",
"上首造字/No.181-上首未来体.ttf": "0093",
"上首造字/No.182-上首象牙体.ttf": "0094",
"上首造字/No.183-上首悦圆体.ttf": "0095",
"上首造字/No.184-上首月牙体.ttf": "0096",
"上首造字/No.185-上首矩正体.ttf": "0097",
"上首造字/No.186-上首鸿运体.ttf": "0098",
"上首造字/No.187-上首童年体.ttf": "0099",
"上首造字/No.188-上首凌宋体.ttf": "0100",
"上首造字/No.189-上首品黑体.ttf": "0101",
"上首造字/No.19-上首简黑极细体.ttf": "0102",
"上首造字/No.190-上首波波手写体.ttf": "0103",
"上首造字/No.191-上首祥瑞体.ttf": "0104",
"上首造字/No.192-上首傲骨体.ttf": "0105",
"上首造字/No.193-上首轩隶体.ttf": "0106",
"上首造字/No.194-上首品宋体.ttf": "0107",
"上首造字/No.195-上首金枝体.ttf": "0108",
"上首造字/No.196-上首游龙体.ttf": "0109",
"上首造字/No.197-上首魔方体.ttf": "0110",
"上首造字/No.198-上首呆呆体.ttf": "0111",
"上首造字/No.199-上首新潮体.ttf": "0112",
"上首造字/No.2-上首维黑体.ttf": "0113",
"上首造字/No.20-上首简宋纤细体.ttf": "0114",
"上首造字/No.200-上首博雅体.ttf": "0115",
"上首造字/No.201-上首鹊桥体.ttf": "0116",
"上首造字/No.202-上首雅倩体.ttf": "0117",
"上首造字/No.203-上首祥瑞手写体.ttf": "0118",
"上首造字/No.204-上首茶颜体.ttf": "0119",
"上首造字/No.205-上首羽刃体.ttf": "0120",
"上首造字/No.206-上首幻灵体.ttf": "0121",
"上首造字/No.207-上首西游体.ttf": "0122",
"上首造字/No.208-上首古居体.ttf": "0123",
"上首造字/No.209-上首润泽体.ttf": "0124",
"上首造字/No.21-上首传奇书法体.ttf": "0125",
"上首造字/No.210-上首醒狮体.ttf": "0126",
"上首造字/No.211-上首冰刃体.ttf": "0127",
"上首造字/No.212-上首玄武体.ttf": "0128",
"上首造字/No.213-上首雪兔体.ttf": "0129",
"上首造字/No.214-上首朱雀体.ttf": "0130",
"上首造字/No.215-上首朗月体.ttf": "0131",
"上首造字/No.216-上首招财猫体.ttf": "0132",
"上首造字/No.217-上首星舰体.ttf": "0133",
"上首造字/No.218-上首正舟体.ttf": "0134",
"上首造字/No.219-上首白虎书法体.ttf": "0135",
"上首造字/No.22-上首简圆体.ttf": "0136",
"上首造字/No.220-上首梧桐体.ttf": "0137",
"上首造字/No.221-上首标题体.ttf": "0138",
"上首造字/No.222-上首阿呆体.ttf": "0139",
"上首造字/No.223-上首月兔体.ttf": "0140",
"上首造字/No.224-上首绫黑体.ttf": "0141",
"上首造字/No.225-上首福禄体.ttf": "0142",
"上首造字/No.226-上首机甲体.ttf": "0143",
"上首造字/No.227-上首晴竹体.ttf": "0144",
"上首造字/No.228-上首光折体.ttf": "0145",
"上首造字/No.229-上首时光体.ttf": "0146",
"上首造字/No.23-上首布丁体.ttf": "0147",
"上首造字/No.230-上首胖虎体.ttf": "0148",
"上首造字/No.231-上首奇妙体.ttf": "0149",
"上首造字/No.232-上首龙腾体.ttf": "0150",
"上首造字/No.233-上首怪兽体.ttf": "0151",
"上首造字/No.234-上首极限体.ttf": "0152",
"上首造字/No.235-上首御风书法体.ttf": "0153",
"上首造字/No.236-上首东坡体.ttf": "0154",
"上首造字/No.237-上首木鱼体.ttf": "0155",
"上首造字/No.238-上首妙趣体.ttf": "0156",
"上首造字/No.239-上首青云体.ttf": "0157",
"上首造字/No.24-上首软糖体.ttf": "0158",
"上首造字/No.240-上首雅隶体.ttf": "0159",
"上首造字/No.241-上首蔷薇体.ttf": "0160",
"上首造字/No.242-上首海浪体.ttf": "0161",
"上首造字/No.243-上首青柏体.ttf": "0162",
"上首造字/No.244-上首墩墩体.ttf": "0163",
"上首造字/No.245-上首无限体.ttf": "0164",
"上首造字/No.246-上首本墨体.ttf": "0165",
"上首造字/No.247-上首祥云体.ttf": "0166",
"上首造字/No.248-上首非凡体.ttf": "0167",
"上首造字/No.249-上首市井体.ttf": "0168",
"上首造字/No.25-上首锐圆极细体.ttf": "0169",
"上首造字/No.250-上首松柏体.ttf": "0170",
"上首造字/No.251-上首水波体.ttf": "0171",
"上首造字/No.252-上首大头手写体.ttf": "0172",
"上首造字/No.253-上首银蛇体.ttf": "0173",
"上首造字/No.254-上首琥珀体.ttf": "0174",
"上首造字/No.255-上首敦煌隶书体.ttf": "0175",
"上首造字/No.256-上首楼兰体.ttf": "0176",
"上首造字/No.257-上首福袋体.ttf": "0177",
"上首造字/No.258-上首惊鸿体.ttf": "0178",
"上首造字/No.259-上首果冻体.ttf": "0179",
"上首造字/No.26-上首悦风体.ttf": "0180",
"上首造字/No.260-上首山岳体.ttf": "0181",
"上首造字/No.261-上首惊云体.ttf": "0182",
"上首造字/No.262-上首江南宋体.ttf": "0183",
"上首造字/No.263-上首蜡笔体.ttf": "0184",
"上首造字/No.264-上首赛博体.ttf": "0185",
"上首造字/No.265-上首心动体.ttf": "0186",
"上首造字/No.266-上首千禧体.ttf": "0187",
"上首造字/No.267-上首柔黑体.ttf": "0188",
"上首造字/No.268-上首黑仪体.ttf": "0189",
"上首造字/No.269-上首涂鸦体.ttf": "0190",
"上首造字/No.27-上首锐棱体.ttf": "0191",
"上首造字/No.270-上首天坛体.ttf": "0192",
"上首造字/No.271-上首麒麟体.ttf": "0193",
"上首造字/No.272-上首青瓦体.ttf": "0194",
"上首造字/No.273-上首风云体.ttf": "0195",
"上首造字/No.274-上首战魂体.ttf": "0196",
"上首造字/No.275-上首招福体.ttf": "0197",
"上首造字/No.276-上首佛陀体.ttf": "0198",
"上首造字/No.277-上首大漠体.ttf": "0199",
"上首造字/No.278-上首绘梦体.ttf": "0200",
"上首造字/No.279-上首东方体.ttf": "0201",
"上首造字/No.28-上首星岩体.ttf": "0202",
"上首造字/No.280-上首英雄体.ttf": "0203",
"上首造字/No.281-上首破冰体.ttf": "0204",
"上首造字/No.282-上首玄机体.ttf": "0205",
"上首造字/No.283-上首舞狮体.ttf": "0206",
"上首造字/No.284-上首北斗体.ttf": "0207",
"上首造字/No.285-上首万福体.ttf": "0208",
"上首造字/No.286-上首云木体.ttf": "0209",
"上首造字/No.287-上首方印体.ttf": "0210",
"上首造字/No.288-上首几何体.ttf": "0211",
"上首造字/No.289-上首江湖书法体.ttf": "0212",
"上首造字/No.29-上首先锋体.ttf": "0213",
"上首造字/No.290-上首书印体.ttf": "0214",
"上首造字/No.291-上首淘气体.ttf": "0215",
"上首造字/No.292-上首暗格体.ttf": "0216",
"上首造字/No.293-上首逸尘体.ttf": "0217",
"上首造字/No.294-上首小新体.ttf": "0218",
"上首造字/No.295-上首荒原体.ttf": "0219",
"上首造字/No.296-上首松针体.ttf": "0220",
"上首造字/No.297-上首墨玄体.ttf": "0221",
"上首造字/No.298-上首沧海书法体.ttf": "0222",
"上首造字/No.299-上首王朝体.ttf": "0223",
"上首造字/No.3-上首方圆体.ttf": "0224",
"上首造字/No.30-上首倾城体.ttf": "0225",
"上首造字/No.300-上首古皇体.ttf": "0226",
"上首造字/No.301-上首言亭体.ttf": "0227",
"上首造字/No.302-上首流金体.ttf": "0228",
"上首造字/No.303-上首墨宋体.ttf": "0229",
"上首造字/No.304-上首龙虎体.ttf": "0230",
"上首造字/No.305-上首点墨体.ttf": "0231",
"上首造字/No.306-上首万圣体.ttf": "0232",
"上首造字/No.307-上首润元体.ttf": "0233",
"上首造字/No.308-上首极速体.ttf": "0234",
"上首造字/No.309-上首栏栅体.ttf": "0235",
"上首造字/No.31-上首简黑纤细体.ttf": "0236",
"上首造字/No.310-上首浮华体.ttf": "0237",
"上首造字/No.311-上首梦想体.ttf": "0238",
"上首造字/No.312-上首诗宋体.ttf": "0239",
"上首造字/No.313-上首亦方体.ttf": "0240",
"上首造字/No.314-上首心愿体.ttf": "0241",
"上首造字/No.315-上首西施体.ttf": "0242",
"上首造字/No.316-上首国美体.ttf": "0243",
"上首造字/No.317-上首润玉体.ttf": "0244",
"上首造字/No.318-上首炫酷体.ttf": "0245",
"上首造字/No.319-上首云烟体.ttf": "0246",
"上首造字/No.32-上首芊芊体.ttf": "0247",
"上首造字/No.320-上首蜀汉体.ttf": "0248",
"上首造字/No.321-上首盛唐体.ttf": "0249",
"上首造字/No.322-上首折扇体.ttf": "0250",
"上首造字/No.323-上首青竹体.ttf": "0251",
"上首造字/No.324-上首胭脂体.ttf": "0252",
"上首造字/No.33-上首方糖体.ttf": "0253",
"上首造字/No.34-上首简黑中细体.ttf": "0254",
"上首造字/No.35-上首积木体.ttf": "0255",
"上首造字/No.36-上首金牛体.ttf": "0256",
"上首造字/No.37-上首朗倩体.ttf": "0257",
"上首造字/No.38-上首漠云体.ttf": "0258",
"上首造字/No.39-上首至尊书法体.ttf": "0259",
"上首造字/No.4-上首秀黑体.ttf": "0260",
"上首造字/No.40-上首星芒体.ttf": "0261",
"上首造字/No.41-上首山川体.ttf": "0262",
"上首造字/No.42-上首芋圆体.ttf": "0263",
"上首造字/No.43-上首星辰体.ttf": "0264",
"上首造字/No.44-上首哥特体.ttf": "0265",
"上首造字/No.45-上首黑岩体 .ttf": "0266",
"上首造字/No.46-上首言黑体.ttf": "0267",
"上首造字/No.47-上首御宋体.ttf": "0268",
"上首造字/No.48-上首三国体.ttf": "0269",
"上首造字/No.49-上首熊猫体.ttf": "0270",
"上首造字/No.5-上首时尚体.ttf": "0271",
"上首造字/No.50-上首国潮体.ttf": "0272",
"上首造字/No.51-上首酷方体.ttf": "0273",
"上首造字/No.52-上首碑楷体.ttf": "0274",
"上首造字/No.53-上首逸飞体.ttf": "0275",
"上首造字/No.54-上首少年体.ttf": "0276",
"上首造字/No.55-上首锋芒体.ttf": "0277",
"上首造字/No.56-上首新艺体.ttf": "0278",
"上首造字/No.57-上首嘉木体.ttf": "0279",
"上首造字/No.58-上首力方体.ttf": "0280",
"上首造字/No.59-上首折言体.ttf": "0281",
"上首造字/No.6-上首宽窄体.ttf": "0282",
"上首造字/No.60-上首本刻体.ttf": "0283",
"上首造字/No.61-上首古宋体.ttf": "0284",
"上首造字/No.62-上首黑风体.ttf": "0285",
"上首造字/No.63-上首迎风手写体.ttf": "0286",
"上首造字/No.64-上首奕星体.ttf": "0287",
"上首造字/No.65-上首山河体.ttf": "0288",
"上首造字/No.66-上首和风体.ttf": "0289",
"上首造字/No.67-上首轩宋体.ttf": "0290",
"上首造字/No.68-上首正雅体.ttf": "0291",
"上首造字/No.69-上首太空体.ttf": "0292",
"上首造字/No.7-上首文正体.ttf": "0293",
"上首造字/No.70-上首夏木体.ttf": "0294",
"上首造字/No.71-上首锐锋体.TTF": "0295",
"上首造字/No.72-上首喵尾体.ttf": "0296",
"上首造字/No.73-上首粉笔体.ttf": "0297",
"上首造字/No.74-上首战戟体.ttf": "0298",
"上首造字/No.75-上首玉立体.ttf": "0299",
"上首造字/No.76-上首战刃体.ttf": "0300",
"上首造字/No.77-上首苍穹书法体.ttf": "0301",
"上首造字/No.78-上首梦黑体.ttf": "0302",
"上首造字/No.79-上首博瀚体.ttf": "0303",
"上首造字/No.8-上首京东体.ttf": "0304",
"上首造字/No.80-上首乐高体.ttf": "0305",
"上首造字/No.81-上首胖墩体.ttf": "0306",
"上首造字/No.82-上首简黑粗体.ttf": "0307",
"上首造字/No.83-上首粗楷体.ttf": "0308",
"上首造字/No.84-上首状元体.ttf": "0309",
"上首造字/No.85-上首元气体.ttf": "0310",
"上首造字/No.86-上首圆盾体.ttf": "0311",
"上首造字/No.87-上首乾坤体.ttf": "0312",
"上首造字/No.88-上首典宋体.ttf": "0313",
"上首造字/No.89-上首金刚体.ttf": "0314",
"上首造字/No.9-上首宽云体.ttf": "0315",
"上首造字/No.90-上首嘻哈体.ttf": "0316",
"上首造字/No.91-上首婷雅体.ttf": "0317",
"上首造字/No.92-上首虎啸体.ttf": "0318",
"上首造字/No.93-上首萌动体.ttf": "0319",
"上首造字/No.94-上首奥丁体.ttf": "0320",
"上首造字/No.95-上首昆仑体.ttf": "0321",
"上首造字/No.96-上首锦宋体.ttf": "0322",
"上首造字/No.97-上首国宋体.ttf": "0323",
"上首造字/No.98-上首趣泡手写体.ttf": "0324",
"上首造字/No.99-上首元正体.ttf": "0325",
"上首造字/Sounso Art.ttf": "0326",
"上首造字/Sounso Beauty.ttf": "0327",
"上首造字/Sounso Cecily.ttf": "0328",
"上首造字/Sounso Diamond.ttf": "0329",
"上首造字/Sounso Earnestly.ttf": "0330",
"上首造字/上首兰亭体.ttf": "0331",
"上首造字/上首凤凰体.ttf": "0332",
"上首造字/上首刀锋体.ttf": "0333",
"上首造字/上首华光体.ttf": "0334",
"上首造字/上首华木体.ttf": "0335",
"上首造字/上首南城体.ttf": "0336",
"上首造字/上首品尚体.ttf": "0337",
"上首造字/上首战马体.ttf": "0338",
"上首造字/上首星语体.ttf": "0339",
"上首造字/上首熔岩体.ttf": "0340",
"上首造字/上首纤云体.ttf": "0341",
"上首造字/上首纯元体.ttf": "0342",
"上首造字/上首苍兰体.ttf": "0343",
"上首造字/上首轩辕体.ttf": "0344",
"上首造字/上首锦云体.ttf": "0345",
"上首造字/上首雅居体.ttf": "0346",
"其他字体/AlimamaDaoLiTi.ttf": "0347",
"其他字体/Hangeuljaemin4-Regular.ttf": "0348",
"其他字体/I.顏體.ttf": "0349",
"其他字体/XCDUANZHUANGSONGTI.ttf": "0350",
"其他字体/qiji-combo.ttf": "0351",
"其他字体/临海隶书.ttf": "0352",
"其他字体/京華老宋体_KingHwa_OldSong.ttf": "0353",
"其他字体/优设标题黑.ttf": "0354",
"其他字体/包图小白体.ttf": "0355",
"其他字体/源界明朝.ttf": "0356",
"其他字体/演示佛系体.ttf": "0357",
"其他字体/站酷快乐体.ttf": "0358",
"其他字体/问藏书房.ttf": "0359",
"其他字体/霞鹜臻楷.ttf": "0360",
"庞门正道-测试/庞门正道标题体.ttf": "0016",
"庞门正道/庞门正道标题体.ttf": "0361",
"王漢宗/王漢宗勘亭流繁.ttf": "0362",
"王漢宗/王漢宗新潮體.ttf": "0363",
"王漢宗/王漢宗波卡體空陰.ttf": "0364",
"王漢宗/王漢宗細黑體繁.ttf": "0365",
"王漢宗/王漢宗綜藝體雙空陰.ttf": "0366",
"王漢宗/王漢宗超明體繁.ttf": "0367",
"王漢宗/王漢宗酷儷海報.ttf": "0368",
"王漢宗/王漢宗顏楷體繁.ttf": "0369"
}
}

View File

@@ -1,15 +1,176 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
生成字体清单文件 生成字体清单文件(稳定 ID 版本)
扫描 fonts/ 目录下的所有字体文件,同时生成: 扫描 fonts/ 目录下的所有字体文件,同时生成:
1. frontend/public/fonts.json 1. frontend/public/fonts.json
2. miniprogram/assets/fonts.json 2. miniprogram/assets/fonts.json
3. miniprogram/assets/fonts.js 3. miniprogram/assets/fonts.js
ID 分配规则:
1. 已存在字体(按 relativePath 识别)保持原 ID 不变。
2. 新增字体按“上次游标”分配新 ID。
3. ID 从 0001 递增到 10000之后循环分配跳过当前正在使用的 ID
""" """
import os import os
import json import json
from pathlib import Path from pathlib import Path
from urllib.parse import unquote, urlparse
FONT_DIR = Path('fonts')
WEB_FONTS_JSON = Path('frontend/public/fonts.json')
MP_FONTS_JSON = Path('miniprogram/assets/fonts.json')
MP_FONTS_JS = Path('miniprogram/assets/fonts.js')
ID_STATE_FILE = Path('scripts/font-id-state.json')
ID_MIN = 1
ID_MAX = 10000
def format_font_id(num):
return str(num).zfill(4)
def parse_font_id(raw):
try:
value = int(str(raw).strip())
except (TypeError, ValueError):
return None
if ID_MIN <= value <= ID_MAX:
return value
return None
def increment_id(num):
return ID_MIN if num >= ID_MAX else num + 1
def normalize_relative_path(raw_path):
if not raw_path:
return None
text = str(raw_path).strip()
if not text:
return None
parsed = urlparse(text)
if parsed.scheme and parsed.netloc:
text = parsed.path
text = unquote(text)
text = text.split('?', 1)[0].split('#', 1)[0]
text = text.replace('\\', '/')
if '/fonts/' in text:
text = text.split('/fonts/', 1)[1]
elif text.startswith('fonts/'):
text = text[len('fonts/'):]
elif text.startswith('/fonts'):
text = text[len('/fonts'):]
text = text.lstrip('/')
return text or None
def load_manifest_id_map(manifest_file):
mapping = {}
if not manifest_file.exists():
return mapping
try:
with open(manifest_file, 'r', encoding='utf-8') as f:
data = json.load(f)
except (OSError, json.JSONDecodeError):
return mapping
if not isinstance(data, list):
return mapping
for item in data:
if not isinstance(item, dict):
continue
relative_path = normalize_relative_path(
item.get('path') or item.get('relativePath')
)
id_num = parse_font_id(item.get('id'))
if relative_path and id_num:
mapping[relative_path] = format_font_id(id_num)
return mapping
def load_id_state(state_file):
default_state = {
'version': 1,
'nextId': ID_MIN,
'pathToId': {},
}
if not state_file.exists():
return default_state
try:
with open(state_file, 'r', encoding='utf-8') as f:
data = json.load(f)
except (OSError, json.JSONDecodeError):
return default_state
if not isinstance(data, dict):
return default_state
path_to_id = {}
raw_map = data.get('pathToId')
if isinstance(raw_map, dict):
for path_key, raw_id in raw_map.items():
relative_path = normalize_relative_path(path_key)
id_num = parse_font_id(raw_id)
if relative_path and id_num:
path_to_id[relative_path] = format_font_id(id_num)
next_id = parse_font_id(data.get('nextId')) or ID_MIN
return {
'version': 1,
'nextId': next_id,
'pathToId': path_to_id,
}
def save_id_state(state, state_file):
state_file.parent.mkdir(parents=True, exist_ok=True)
normalized_map = dict(sorted(state['pathToId'].items(), key=lambda x: x[0]))
data = {
'version': 1,
'nextId': state['nextId'],
'pathToId': normalized_map,
}
with open(state_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
f.write('\n')
def bootstrap_state_mappings(state):
# 优先级state 文件 > 小程序现有清单 > web 现有清单
for manifest_path in (MP_FONTS_JSON, WEB_FONTS_JSON):
mapping = load_manifest_id_map(manifest_path)
for relative_path, font_id in mapping.items():
state['pathToId'].setdefault(relative_path, font_id)
if parse_font_id(state.get('nextId')) is None:
state['nextId'] = ID_MIN
if state['nextId'] == ID_MIN and state['pathToId']:
max_id = max(parse_font_id(v) for v in state['pathToId'].values())
state['nextId'] = increment_id(max_id)
def allocate_new_id(start_id, used_ids):
candidate = start_id
for _ in range(ID_MAX):
if candidate not in used_ids:
return candidate, increment_id(candidate)
candidate = increment_id(candidate)
raise RuntimeError('可用 ID 已耗尽0001-10000 全部被当前字体占用)')
def scan_fonts(font_dir='fonts'): def scan_fonts(font_dir='fonts'):
"""扫描字体目录,返回字体信息列表""" """扫描字体目录,返回字体信息列表"""
@@ -45,14 +206,48 @@ def scan_fonts(font_dir='fonts'):
fonts.append(font_info) fonts.append(font_info)
# 统一排序后分配 4 位数字 id0001、0002... # 固定顺序,保证分配冲突时结果稳定
fonts = sorted(fonts, key=lambda x: (x['category'], x['name'], x['filename'])) fonts = sorted(fonts, key=lambda x: (x['category'], x['name'], x['filename'], x['relativePath']))
for index, font in enumerate(fonts, start=1):
font['id'] = f"{index:04d}"
return fonts return fonts
def assign_stable_ids(fonts):
state = load_id_state(ID_STATE_FILE)
bootstrap_state_mappings(state)
cursor = parse_font_id(state.get('nextId')) or ID_MIN
used_ids = set()
new_count = 0
for font in fonts:
relative_path = font['relativePath']
existing_id = state['pathToId'].get(relative_path)
existing_num = parse_font_id(existing_id)
if existing_num and existing_num not in used_ids:
assigned_num = existing_num
else:
if existing_num and existing_num in used_ids:
print(
f"警告: 发现 ID 冲突,路径 {relative_path} 原 ID {existing_id} 将重新分配。"
)
assigned_num, cursor = allocate_new_id(cursor, used_ids)
state['pathToId'][relative_path] = format_font_id(assigned_num)
new_count += 1
used_ids.add(assigned_num)
font['id'] = format_font_id(assigned_num)
state['nextId'] = cursor
save_id_state(state, ID_STATE_FILE)
return {
'newCount': new_count,
'nextId': format_font_id(cursor),
'stateFile': str(ID_STATE_FILE),
}
def build_manifest(fonts, path_prefix): def build_manifest(fonts, path_prefix):
"""根据路径前缀构建对外清单""" """根据路径前缀构建对外清单"""
prefix = f"/{str(path_prefix or '').strip('/')}" prefix = f"/{str(path_prefix or '').strip('/')}"
@@ -91,18 +286,25 @@ def write_fonts_js(fonts, output_file):
def main(): def main():
"""主函数""" """主函数"""
# 扫描字体(唯一来源:仓库根目录 fonts/ # 扫描字体(唯一来源:仓库根目录 fonts/
fonts = scan_fonts('fonts') fonts = scan_fonts(str(FONT_DIR))
print(f"找到 {len(fonts)} 个字体文件") print(f"找到 {len(fonts)} 个字体文件")
allocation_result = assign_stable_ids(fonts)
print(
f"ID 分配完成:新增 {allocation_result['newCount']} 个,"
f"下次起始 ID {allocation_result['nextId']}"
f"状态文件 {allocation_result['stateFile']}"
)
# Web 清单:统一指向根目录 fonts # Web 清单:统一指向根目录 fonts
web_fonts = build_manifest(fonts, '/fonts') web_fonts = build_manifest(fonts, '/fonts')
write_fonts_json(web_fonts, 'frontend/public/fonts.json') write_fonts_json(web_fonts, str(WEB_FONTS_JSON))
# 小程序清单:同样指向根目录 fonts与 web 共用一份字体目录) # 小程序清单:同样指向根目录 fonts与 web 共用一份字体目录)
miniprogram_fonts = build_manifest(fonts, '/fonts') miniprogram_fonts = build_manifest(fonts, '/fonts')
write_fonts_json(miniprogram_fonts, 'miniprogram/assets/fonts.json') write_fonts_json(miniprogram_fonts, str(MP_FONTS_JSON))
write_fonts_js(miniprogram_fonts, 'miniprogram/assets/fonts.js') write_fonts_js(miniprogram_fonts, str(MP_FONTS_JS))
# 统计信息 # 统计信息
categories = {} categories = {}