diff --git a/HISTORY.md b/HISTORY.md index 3be2cfa82984d33467e3743f0b72f197e0fd5200..731fd0b832419c735fbd30850576f0a692aed958 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,3 +13,7 @@ # 2023-08-25 - feat: 字库新增 1800 余字 + +# 2023-09-12 + +- feat: 新增自行添补汉字的工具和文档 diff --git a/demo/.docker/database/init.sql b/demo/.docker/database/init.sql index 81d71c94375940a8f0e5bfae2dff3d53a3285a03..07ab769013686554f06a66b63f88fb931f203dca 100644 --- a/demo/.docker/database/init.sql +++ b/demo/.docker/database/init.sql @@ -9698,3 +9698,11 @@ INSERT INTO `rare_characters` VALUES ('2023082407762','9bb380ed','UNICODE','𪮅 INSERT INTO `rare_characters` VALUES ('2023082407763','9bb380ed','PUA','','','','','','','10','','2023-08-24 16:16:30.456000','2023-08-24 16:16:30.456000'); INSERT INTO `rare_characters` VALUES ('2023082407764','9bb380ed','PINYIN','TIAO','','','2','','','10','','2023-08-24 16:16:30.456000','2023-08-24 16:16:30.456000'); INSERT INTO `rare_characters` VALUES ('2023082407765','9bb380ed','SPLIT','扌条','','','','','','10','','2023-08-24 16:16:30.456000','2023-08-24 16:16:30.456000'); + +INSERT INTO `rare_characters` VALUES ('2023091200001','802cdf67','UNICODE','𫰠','2BC20','undefined','','','','10','"{""ncr_code_hex"":""2BC20""}"',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +INSERT INTO `rare_characters` VALUES ('2023091200002','802cdf67','PINYIN','DANG','','','1','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +INSERT INTO `rare_characters` VALUES ('2023091200003','802cdf67','SPLIT','女当','','','','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); + +INSERT INTO `rare_characters` VALUES ('2023091200004','25be47d5','UNICODE','𱖎','3158E','undefined','','','','10','"{""ncr_code_hex"":""3158E""}"',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +INSERT INTO `rare_characters` VALUES ('2023091200005','25be47d5','PINYIN','AO','','','4','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +INSERT INTO `rare_characters` VALUES ('2023091200006','25be47d5','SPLIT','土夭','','','','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); diff --git a/demo/web/public/fonts/AlibabaPuHuiTi-3-55-Regular.ttf b/demo/web/public/fonts/AlibabaPuHuiTi-3-55-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a6eaf3613ee1160caf604ab3f11b303fdb5f8668 Binary files /dev/null and b/demo/web/public/fonts/AlibabaPuHuiTi-3-55-Regular.ttf differ diff --git a/demo/web/public/fonts/AlibabaPuHuiTi-3-55-RegularL3.ttf b/demo/web/public/fonts/AlibabaPuHuiTi-3-55-RegularL3.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3c82b62b9b5bf88f37153a937c737d39f7f9a37e Binary files /dev/null and b/demo/web/public/fonts/AlibabaPuHuiTi-3-55-RegularL3.ttf differ diff --git a/demo/web/public/fonts/RareWordsFonts.ttf b/demo/web/public/fonts/RareWordsFonts.ttf index ba4686df8dcc6c0e68582601e4e970d0d2cd14a1..dec0214763fc6c38d9c6d2e4f10c6a3eba4987e3 100644 Binary files a/demo/web/public/fonts/RareWordsFonts.ttf and b/demo/web/public/fonts/RareWordsFonts.ttf differ diff --git a/docs/Add_chars_into_ZDatas.md b/docs/Add_chars_into_ZDatas.md new file mode 100644 index 0000000000000000000000000000000000000000..2f60c544e8f8d7dd9796fbc948c5204ef2804343 --- /dev/null +++ b/docs/Add_chars_into_ZDatas.md @@ -0,0 +1,115 @@ +# Z字库新增汉字指南 + +Z 字库关于汉字数据有三个方面: + +1. 输入键盘元数据,录入汉字拼音和拆字信息 (`frontend/utils/src/contants/zdata-local.ts`) +2. Z字库字体文件,包含了所有汉字的字体 (`demo/web/public/fonts/RareWordsFonts.ttf`) +3. 后端数据库 `SQL` 文件,存放汉字插入 `SQL` 指令 (`demo/.docker/database/init.sql`) + +因此新增字库也就是修改以上三个文件,下面我逐一介绍如何以上三个文件应该如何新增汉字。 + +## 输入键盘元数据 + +输入键盘元数据结构: + +```json +{ + charId: '66c867', + unicodeChar: '䶮', + unicodeCodePoint: '4DAE', + pinYinChars: [{ char: 'YAN3' }], + splitChars: [{ char: '龙天' }], + weight: '10', +} +``` + +属性说明: +| 属性名 | 类型 | 说明 | +| ---------- | ---------- | ---------------- | +| `charId` | string | 汉字唯一 ID | +| `unicodeChar` | string | 汉字原字,汉字可复制字符 | +| `unicodeCodePoint` | string | 汉字 unicode 编码 | +| `pinYinChars` | {char: string}[] | 拼音,支持多个 | +| `splitChars` | {char: string}[] | 拆字,支持多个 | +| `weight` | number | 汉字优先级 | + +下面是各个属性的获取方法: + ++ `charId` 生成可以使用项目目录下的`tools`工具,该工具会自动生成8位唯一`ID`,并且和已有数据做查重,确保ID唯一性,使用方法如下: + +```bash +cd tools && npm install && npm run generate-char-id +# 输出结果:新的charId: 47ff0467 +``` ++ `unicodeCodePoint、pinYinChars、splitChars` 可以前往[字海网](http://www.yedict.com/)查询得到 + +![插入图片](https://mdn.alipayobjects.com/huamei_2fq7mt/afts/img/A*0zzHTqBj1xMAAAAAAAAAAAAADh58AQ/original) + ++ `weight` 是字符优先级,优先级越高在用户输入的时候就会被优先推荐,这个值通常反映了字符的常用程度,默认是`10` + + +> !!!Z字库数据还有一个`fontUrl`的属性,该字段是输入组件自动加载的生僻字字体cdn地址,在生成新的Z字库字体文件后,需要更新该地址(自行上传字体文件并替换该地址),否则输入组件无法加载最新的字体,新加的汉字就没法在输入组件中展示、 + +## Z字库字体文件 +新增了汉字后需要生成这些新增汉字相应的字体,新增的大部分汉字在[阿里巴巴普惠体3.0](https://fonts.alibabagroup.com/#/font)里应该都是有的(可以在`word/wps`这些软件中设置文本字体为普惠体,看看新增的汉字是否能正常展示,以此确定该汉字是否在普惠体中). + +下面我分别阐述新增的汉字**在普惠体存在字体**和**在普惠体不存在字体**两种情况的处理办法: + +### 新增汉字字体在普惠体中 +在完成**输入键盘元数据**的补充以后, 可以使用项目目录下的`tools`工具,该工具会根据你修改好的Z字库元数据从普惠体分离出新增汉字的字体并合并到原有字体中,使用方法如下: +```bash +cd tools && npm run generate-font + +# 输出结果: +# 开始生成字体... +# 生存的字体包含如下汉字:"𮧵 𬀩 𤩽 㑇 𣲗 𰵞 𠇔 冄 䒟 𡛓 𡝗 𪻐 ... +# 存在以下字体生成失败:"" +``` +执行脚本后 `demo/web/public/fonts/RareWordsFonts.ttf` 会被更新 + +你可以将 `demo/web` 运行起来并修改需要预览的汉字(`demo/web/docs/display/index.tsx`)来检测生成的字体是否生效,如果汉字能在预览demo里正常展示即代表生成的字体是可以使用的。 + +![字体预览](https://mdn.alipayobjects.com/huamei_2fq7mt/afts/img/A*pGqiQq6I6j4AAAAAAAAAAAAADh58AQ/original) + +也可以使用 [开源字体预览工具](http://blog.luckly-mjw.cn/tool-show/iconfont-preview/index.html) 或者 [Glyph3](https://glyphsapp.com/buy) 这类字体管理工具预览字体,检查新增的汉字是否在最新的字体中。 + + +### 新增汉字字体不在普惠体中 +需要新增的汉字不在阿里巴巴普惠体中(比如鸟甲nia这种字),处理办法稍微有些麻烦,你需要自己创建一个字体并合并到Z字库字体中。 + +创建字体可以使用 [Glyph3](https://glyphsapp.com/buy) 或者 [FontCreator](https://www.high-logic.com/font-editor/fontcreator) , +有一些开源工具也是可以创建字体的,比如 [fonteditor](https://github.com/ecomfe/fonteditor),甚至可以将svg或者图片转化成字体,编辑字体的字形数据,最后导出一份字体ttf文件。 + +![字体编辑](https://mdn.alipayobjects.com/huamei_2fq7mt/afts/img/A*PU60TpUkjg8AAAAAAAAAAAAADh58AQ/original) + +创建字体文件以后,可以使用项目目录下的`tools`工具将新增的字体合并到Z字库字体中,使用之前您需要修改 `tools/mergeFont.js` 里的待合并字体的存储地址 `tempFontFilePath`,修改之后使用方法如下: +```bash +cd tools && npm run merge-font + +# 输出结果: +# 开始合并字体... +# 合并后的字体存放地址:xxx +``` + +> !!! 如果自己添补了新的汉字,需要在生成新的字体后将字体上传到cdn,并在使用的时候替换掉官方的字体cdn地址。 +> `https://mdn.alipayobjects.com/huamei_seif62/afts/file/A*63IqSLzYAdEAAAAAAAAAAAAADh18AQ/RareWordsFonts-v1.0.14.ttf` 的地址是蚂蚁Z字库的官方字体CDN地址,定期更新,不包含您自己添加的新增的汉字的字体。 + +## 后端数据库SQL文件 + +最后一步就是生成Z字库后端数据库SQL插入指令,然后删除`docker`运行起来的`mysql`实例,并重新启动`docker`即可,你可以使用项目目录下的 `tools/generateSql.js` 工具生成SQL指令,使用方法如下: +```bash +cd tools && npm run generate-sql + +# 输出结果: +# 开始生成SQL... +# 新增字符的SQL: +# INSERT INTO `rare_characters` VALUES ('2023091200001','802cdf67','UNICODE','𫰠','2BC20','undefined','','','','10','"{""ncr_code_hex"":""2BC20""}"',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +# INSERT INTO `rare_characters` VALUES ('2023091200002','802cdf67','PINYIN','DANG','','','1','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); +# INSERT INTO `rare_characters` VALUES ('2023091200003','802cdf67','SPLIT','女当','','','','','','10','',2023-09-12 16:19:38.975,2023-09-12 16:19:38.975); + +# SQL更新后存放地址:xxx +``` + +最后就是登录 `docker` 删除mysql实例并重新执行`docker-compose up`,这样本地数据库会重新执行最新的sql指令,达到更新本地数据库的目的: + +![删除sql实例](https://mdn.alipayobjects.com/huamei_seif62/afts/img/A*BBwfTrquJXAAAAAAAAAAAAAADh18AQ/original) diff --git a/frontend/utils/src/contants/index.ts b/frontend/utils/src/contants/index.ts index 4b1202facdd3f4042bb281cdc9adccf303341f07..5715033b91035e0420386c326643968179c3ccd7 100644 --- a/frontend/utils/src/contants/index.ts +++ b/frontend/utils/src/contants/index.ts @@ -1,7 +1,7 @@ /** * 默认字体下载地址 */ -export const DEFAULT_FONT_URL = 'https://mdn.alipayobjects.com/huamei_2fq7mt/afts/file/A*hHI6SahdSqMAAAAAAAAAAAAADh58AQ/RareWordsFonts-v1.0.13.ttf'; +export const DEFAULT_FONT_URL = 'https://mdn.alipayobjects.com/huamei_seif62/afts/file/A*63IqSLzYAdEAAAAAAAAAAAAADh18AQ/RareWordsFonts-v1.0.14.ttf'; /** * 字库缓存key值 diff --git a/frontend/utils/src/contants/zdata-local.ts b/frontend/utils/src/contants/zdata-local.ts index 18110491e3fbd44b1c4a27b263b2d6c7f3ac7237..84579a9109b561bb780af8f52a28ad029bc866c6 100644 --- a/frontend/utils/src/contants/zdata-local.ts +++ b/frontend/utils/src/contants/zdata-local.ts @@ -1,7 +1,7 @@ export const ZDATAS = { version: '1.0.5', fontUrl: - 'https://mdn.alipayobjects.com/huamei_2fq7mt/afts/file/A*hHI6SahdSqMAAAAAAAAAAAAADh58AQ/RareWordsFonts-v1.0.13.ttf', + 'https://mdn.alipayobjects.com/huamei_seif62/afts/file/A*63IqSLzYAdEAAAAAAAAAAAAADh18AQ/RareWordsFonts-v1.0.14.ttf', datas: [ { charId: 'd88cab', @@ -15312,5 +15312,21 @@ export const ZDATAS = { splitChars: [{ char: '扌条' }], weight: 10, }, + { + charId: '802cdf67', + unicodeChar: '𫰠', + unicodeCodePoint: '2BC20', + pinYinChars: [{ char: 'DANG1' }], + splitChars: [{ char: '女当' }], + weight: 10, + }, + { + charId: '25be47d5', + unicodeChar: '𱖎', + unicodeCodePoint: '3158E', + pinYinChars: [{ char: 'AO4' }], + splitChars: [{ char: '土夭' }], + weight: 10, + }, ], }; diff --git a/tools/generateCharId.js b/tools/generateCharId.js new file mode 100644 index 0000000000000000000000000000000000000000..5826c7108550032a01c5af478856f4fee72d349f --- /dev/null +++ b/tools/generateCharId.js @@ -0,0 +1,48 @@ +/** + * Z字库唯一ID生成脚本 + */ +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +const ZDATAS_FILE_PATH = path.join(__dirname, '../frontend/utils/src/contants/zdata-local.ts') + +// 生成新的charId +function generateId() { + const randomBytes = crypto.randomBytes(4); // 生成4个字节的随机字节数组 + const uniqueId = randomBytes.toString('hex').slice(0, 8); // 将字节数组转换成16进制字符串,并截取前8位 + return uniqueId; +} + +/** + * 解析zdata数据 + * @param {string} filePath zdata数据文件地址 + * @returns + */ +function readAndParseJson(filePath) { + // 读取文件内容 + const fileContent = fs.readFileSync(filePath, 'utf-8'); + // 提取 JSON 数据 + const jsonData = fileContent.match(/export\s*const\s*ZDATAS\s*=\s*(.*);/s)[1]; + + if (!jsonData || jsonData.length < 3) { + throw new Error('Failed to parse JSON data'); + } + + // 解析 JSON 数据 + const parsedData = eval('(' + jsonData + ')'); + return parsedData || {}; +} + +/** + * 检查和已有的charId是否重复 + * @returns charId + */ +function generateUniqueId() { + const charId = generateId(); + const { datas = [] } = readAndParseJson(ZDATAS_FILE_PATH); + const existedItem = datas.find(item => item.charId === charId); + return existedItem ? generateUniqueId() : charId; +} + +console.log(`新的charId: ${generateUniqueId()}`); diff --git a/tools/generateSql.js b/tools/generateSql.js new file mode 100644 index 0000000000000000000000000000000000000000..5abeeb648492dcfec3e6a4d5f2db105354fdebd8 --- /dev/null +++ b/tools/generateSql.js @@ -0,0 +1,159 @@ +/** + * 根据Z字库数据生成对应的SQL文件 + */ +const { format } = require('date-fns'); +const fs = require('fs'); +const path = require('path'); + +const currentDate = new Date(); +const formattedDate = format(currentDate, 'yyyyMMdd'); +const formattedDateAndTime = format(currentDate, 'yyyy-MM-dd HH:mm:ss.SSS'); + + + +let SQLIDStartIndex = 0; +const newSQLDataArr = []; +const ZDATAS_FILE_PATH = path.join(__dirname, '../frontend/utils/src/contants/zdata-local.ts'); +const SQL_FILE_PATH = path.join(__dirname, '../demo/.docker/database/init.sql'); + + + +// 将顺序转化成主键ID +function transIndexToSqlId(index) { + return formattedDate + index.toString().padStart(5, '0'); +} + +// 生成插入UNICODE项的SQL +function generateUNICODEInsert(word) { + SQLIDStartIndex += 1; + return `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + word.charId + }','UNICODE','${word.unicodeChar}','${word.unicodeCodePoint}','${ + word.unicodeNcrCode + }','','','','${word.weight}','\"{\"\"ncr_code_hex\"\":\"\"${ + word.unicodeCodePoint + }\"\"}\"',${formattedDateAndTime},${formattedDateAndTime});`; +} + +// 生成插入PUA项的SQL +function generatePUAInsert(word) { + SQLIDStartIndex += 1; + // return `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + // word.charId + // }','PUA','${word.puaChar}','${ + // word.puaCodePoint + // }','','','','','${word.weight}','',${formattedDateAndTime},${formattedDateAndTime});`; + return `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + word.charId + }','PUA','','','','','','','${word.weight}','',${formattedDateAndTime},${formattedDateAndTime});`; +} + +// 生成插入PINYIN项的SQL +function generatePINYINInsert(word) { + const result = []; + word.pinYinChars.forEach(item => { + if (item.char) { + SQLIDStartIndex += 1; + const letter = item.char.match(/[a-zA-Z]+/) ? item.char.match(/[a-zA-Z]+/)[0] : ''; + const number = item.char.match(/\d+/) ? item.char.match(/\d+/)[0] : ''; + result.push( + `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + word.charId + }','PINYIN','${letter}','','','${number}','','','${word.weight}','',${formattedDateAndTime},${formattedDateAndTime});` + ); + } + }); + return result; +} + +// 生成插入SPLIT项的SQL +function generateSPLITInsert(word) { + const result = []; + word.splitChars.forEach(item => { + SQLIDStartIndex += 1; + result.push( + `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + word.charId + }','SPLIT','${ + item.char + }','','','','','','${word.weight}','',${formattedDateAndTime},${formattedDateAndTime});` + ); + }); + return result; +} + +// 生成插入TRADITIONAL项的SQL +function generateTRADITIONALInsert(word) { + const result = []; + word.tranditional.forEach(item => { + SQLIDStartIndex += 1; + const unicode = getChineseUnicode(item.char); + result.push( + `INSERT INTO \`rare_characters\` VALUES ('${transIndexToSqlId(SQLIDStartIndex)}','${ + word.charId + }','TRADITIONAL','${item.char}','${item.code || unicode}','${getChineseNcrCode( + item.char + )}','','','','${word.weight}','\"{\"\"ncr_code_hex\"\":\"\"${ + item.code || unicode + }\"\"}\"',${formattedDateAndTime},${formattedDateAndTime});` + ); + }); + return result; +} + +/** + * 解析zdata数据 + * @param {string} filePath zdata数据文件地址 + * @returns + */ +function readAndParseJson(filePath) { + // 读取文件内容 + const fileContent = fs.readFileSync(filePath, 'utf-8'); + // 提取 JSON 数据 + const jsonData = fileContent.match(/export\s*const\s*ZDATAS\s*=\s*(.*);/s)[1]; + + if (!jsonData || jsonData.length < 3) { + throw new Error('Failed to parse JSON data'); + } + + // 解析 JSON 数据 + const parsedData = eval('(' + jsonData + ')'); + return parsedData || {}; +} + +console.log('开始生成SQL...'); + +const sqlContent = fs.readFileSync(SQL_FILE_PATH, 'utf-8'); +const { datas = [] } = readAndParseJson(ZDATAS_FILE_PATH); +const newAddedWords = datas.filter(item => sqlContent.indexOf(item.charId) < 0); + +newAddedWords.forEach((word, index) => { + if (word.unicodeChar && word.unicodeCodePoint) { + newSQLDataArr.push(generateUNICODEInsert(word)); + } + if (word.puaChar && word.puaCodePoint) { + newSQLDataArr.push(generatePUAInsert(word)); + } + if (word.pinYinChars.length > 0) { + generatePINYINInsert(word).forEach(item => { + newSQLDataArr.push(item); + }); + } + if (word.splitChars.length > 0) { + generateSPLITInsert(word).forEach(item => { + newSQLDataArr.push(item); + }); + } + if (word.tranditional) { + generateTRADITIONALInsert(word).forEach(item => { + newSQLDataArr.push(item); + }); + } + newSQLDataArr.push(''); +}); + +console.log(`新增字符的SQL:\n` + newSQLDataArr.join('\n')) + +fs.appendFileSync(SQL_FILE_PATH, '\n' + newSQLDataArr.join('\n')); + +console.log('SQL更新后存放地址:' + SQL_FILE_PATH); diff --git a/tools/mergeFont.js b/tools/mergeFont.js new file mode 100644 index 0000000000000000000000000000000000000000..0ef79dff83ea99aec243c503220d4b7de3f8c29b --- /dev/null +++ b/tools/mergeFont.js @@ -0,0 +1,30 @@ +/** + * 字体合并到Z字库字体脚本 + */ +const fs = require('fs'); +const path = require('path'); +const fontCarrier = require('ant-font-carrier'); +const he = require('he'); + +const rareWordsFilePath = path.join(__dirname, '../demo/web/public/fonts/RareWordsFonts.ttf'); +const tempFontFilePath = path.join(__dirname, './tempFont.ttf'); // 自己新增的字符字体文件地址 + + +const rareWordsFont = fontCarrier.transfer(rareWordsFilePath); +const tempFont = fontCarrier.transfer(tempFontFilePath); +const allGlyphs = tempFont.allGlyph(); + +console.log('开始合并字体...'); + +Object.keys(allGlyphs).forEach(key => { + rareWordsFont.setGlyph(key, allGlyphs[key]); +}) + +console.log('合并后的字体存放地址:' + rareWordsFilePath); +rareWordsFont.setFontface({ + fontFamily: 'rare-words-font', + unitsPerEm: 1024, + ascent: 812, + descent: -212, +}); +rareWordsFont.output({ path: rareWordsFilePath, types: ['ttf'] }); diff --git a/tools/package.json b/tools/package.json new file mode 100644 index 0000000000000000000000000000000000000000..32c8fb4ba6f0a886ec751b87774f8915b2e1b3d1 --- /dev/null +++ b/tools/package.json @@ -0,0 +1,23 @@ +{ + "name": "z-data-tools", + "version": "1.0.0", + "description": "蚂蚁Z字库生僻字解决方案工具", + "main": "index.js", + "scripts": { + "generate-char-id": "node generateCharId.js", + "generate-font": "node splitAlibabaSubFontAndMergeZData.js", + "merge-font": "node mergeFont.js", + "generate-sql": "node generateSql.js" + }, + "author": "", + "license": "ISC", + "repository": "https://github.com/alipay/Z-RareCharacterSolution.git", + "devDependencies": { + "ant-font-carrier": "^0.0.2", + "crypto": "^1.0.1", + "date-fns": "^2.30.0", + "he": "^1.2.0", + "ts-node": "^10.9.1", + "uuid": "^9.0.0" + } +} diff --git a/tools/splitAlibabaSubFontAndMergeZData.js b/tools/splitAlibabaSubFontAndMergeZData.js new file mode 100644 index 0000000000000000000000000000000000000000..8256c73aebfff1425971081696db509ded6391e3 --- /dev/null +++ b/tools/splitAlibabaSubFontAndMergeZData.js @@ -0,0 +1,111 @@ +/** + * 根据Z字库数据生成字体脚本 + */ +const fs = require('fs'); +const path = require('path'); +const fontCarrier = require('ant-font-carrier'); +const he = require('he'); + +/** 生成字体文件 */ +console.log('开始生成字体...'); +const regularFilePath = path.join( + __dirname, + '../demo/web/public/fonts/AlibabaPuHuiTi-3-55-Regular.ttf', +); +const regularL3FilePath = path.join( + __dirname, + '../demo/web/public/fonts/AlibabaPuHuiTi-3-55-RegularL3.ttf', +); +const rareWordsFilePath = path.join( + __dirname, + '../demo/web/public/fonts/RareWordsFonts.ttf', +); + +const newFont = fontCarrier.create({ id: 'RareWordsFont' }); +const regularFont = fontCarrier.transfer(regularFilePath); +const regularL3Font = fontCarrier.transfer(regularL3FilePath); +const rareWordsFont = fontCarrier.transfer(rareWordsFilePath); +const fontGetFailArr = []; +const ZDATAS_FILE_PATH = path.join( + __dirname, + '../frontend/utils/src/contants/zdata-local.ts', +); + +/** + * 根据编码查找对应的字体 + * 查找顺序为 RareWordsFonts.ttf + * -> AlibabaPuHuiTi-3-55-Regular.ttf + * -> AlibabaPuHuiTi-3-55-RegularL3.ttf + * @param {string} text 汉字原字 + * @param {string} code 汉字编码 + */ +function getFontDataByCode(text = '', code = '') { + const ncrCode = he.encode(text, { decimal: true }); + try { + const rareWordsGs = rareWordsFont.getGlyph(ncrCode); + if (!rareWordsGs) { + const regularGs = regularFont.getGlyph(ncrCode); + if (!regularGs) { + const regularL3Gs = regularL3Font.getGlyph(ncrCode); + if (!regularL3Gs) { + throw new Error('字体不存在'); + } else { + newFont.setGlyph(ncrCode, regularL3Gs); + } + } else { + newFont.setGlyph(ncrCode, regularGs); + } + } else { + newFont.setGlyph(ncrCode, rareWordsGs); + } + } catch (err) { + fontGetFailArr.push({ text, code }); + console.log( + `根据编码查找对应的字体失败, text: ${text}, code: ${code}, error: ${err.toString()}`, + ); + } +} + +/** + * 解析zdata数据 + * @param {string} filePath zdata数据文件地址 + * @returns + */ +function readAndParseJson(filePath) { + // 读取文件内容 + const fileContent = fs.readFileSync(filePath, 'utf-8'); + // 提取 JSON 数据 + const jsonData = fileContent.match(/export\s*const\s*ZDATAS\s*=\s*(.*);/s)[1]; + + if (!jsonData || jsonData.length < 3) { + throw new Error('Failed to parse JSON data'); + } + + // 解析 JSON 数据 + const parsedData = eval('(' + jsonData + ')'); + return parsedData || {}; +} + +const { datas = [] } = readAndParseJson(ZDATAS_FILE_PATH); +datas.forEach((word) => { + getFontDataByCode(word.unicodeChar, word.unicodeCodePoint); +}); + +console.log( + '生存的字体包含如下汉字:' + + JSON.stringify(datas.map((item) => item.unicodeChar).join(' ')), +); +console.log( + '存在以下字体生成失败:' + + JSON.stringify( + fontGetFailArr.map((item) => `${item.text} ${item.code}`).join(', '), + ), +); +console.log('生成的字体存放地址:' + rareWordsFilePath); +newFont.setFontface({ + fontFamily: 'rare-words-font', + unitsPerEm: 1024, + ascent: 812, + descent: -212, +}); +newFont.output({ path: rareWordsFilePath, types: ['ttf'] }); diff --git a/tools/tempFont.ttf b/tools/tempFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1f25feea72f4c40a53167e837d00067c582df910 Binary files /dev/null and b/tools/tempFont.ttf differ