diff --git a/README.md b/README.md index 75d4916..57f0cea 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,13 @@ ui 需要展现一些特定的字体,但直接引入字体包又过大,于 ### 动态生成字体 -http://127.0.0.1:3000/generate_fonts_dynamically/font/1584688387272/优设标题黑.ttf +![generate_fonts_dynamically](./doc_img/api/generate_fonts_dynamically.jpg) + +#### 请注意 + +只支持生成 .ttf .eot .woff .svg 这几种格式 + +如果 text 参数为空将会返回整个字体包 ## 写项目时遇到的问题 diff --git a/doc_img/api/generate_fonts_dynamically.jpg b/doc_img/api/generate_fonts_dynamically.jpg new file mode 100644 index 0000000..93c3838 Binary files /dev/null and b/doc_img/api/generate_fonts_dynamically.jpg differ diff --git a/src/app.controller.ts b/src/app.controller.ts index 522b0f6..ce7b86e 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -11,6 +11,7 @@ import { AppService } from './app.service'; import { join } from 'path'; import { promises as fs } from 'fs'; import { Request } from 'express'; +import { Stream } from 'stream'; @Controller() export class AppController { constructor(private readonly appService: AppService) {} @@ -30,13 +31,28 @@ export class AppController { /** 压缩字体 */ @Get('generate_fonts_dynamically*') - generate_fonts_dynamically( - @Req() req:Request, + async generate_fonts_dynamically( + @Req() req: Request, + @Res() res, @Query('text') text: string, @Query('font') font: string, @Query('temp') temp: string, ) { - const format=req.url.match(/\.(.*)\?/)[1] - return this.appService.generate_fonts_dynamically(text, font,temp,format); + console.time() + const type = req.url.match(/\.(.*)\?/)[1]; + const file = await this.appService.generate_fonts_dynamically( + text, + font, + temp, + type, + ); + res.set({ + 'Content-Type': `font/${type}`, + }); + const bufferStream = new Stream.PassThrough(); + bufferStream.end(file); + bufferStream.pipe(res); + console.timeEnd() + } } diff --git a/src/app.service.ts b/src/app.service.ts index ca4f78f..03c132b 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -2,7 +2,9 @@ import { Injectable } from '@nestjs/common'; import Fontmin from 'fontmin'; const { zip } = require('zip-a-folder'); import { join } from 'path'; +import crypto from 'crypto'; import { config } from './config'; +import { promises as fs } from 'fs'; @Injectable() export class AppService { font_min(text: string, font: string) { @@ -47,12 +49,83 @@ export class AppService { }); } - generate_fonts_dynamically( + async generate_fonts_dynamically( text: string, font: string, temp: string, - format: string, + type: string, ) { - return 11; + const hash = crypto.createHash('md5'); + hash.update(`${type}${font}${text}`); + const hash_str = hash.digest('hex'); + const srcPath = `./src/font/${font}.ttf`; // 字体源文件 + const outPath = `asset/dynamically/${hash_str}`; + const destPath = `./${outPath}`; // 输出路径 + + const full_path = join(__dirname, '../../', destPath, `${font}.${type}`); + /** 需要持久化 */ + if (temp !== 'true') { + try { + return await fs.readFile(full_path); + } catch (error) { + console.log(`开始生成 ${full_path}`,error); + } + } + // 初始化 + const fontmin = new Fontmin() + .src(srcPath) // 输入配置 + .use( + Fontmin.glyph({ + text, // 所需文字 + }), + ); + + if ('eot' === type) { + fontmin.use(Fontmin.ttf2eot()); // eot 转换插件 + } + if ('woff' === type) { + fontmin.use(Fontmin.ttf2woff()); // eot 转换插件 + } + if ('svg' === type) { + fontmin.use(Fontmin.ttf2svg()); // eot 转换插件 + } + /** 缓存数据 */ + if (temp !== 'true') { + fontmin.dest(destPath) + } + + + // 执行 + return new Promise((resolve, reject) => { + fontmin.run(async function(err, files, stream) { + if (err) { + // 异常捕捉 + reject(err); + } else { + const buffer = files.filter(f => + /** 筛选需要的类型 */ + (f.history[f.history.length - 1] as string).endsWith(type), + )[0]._contents; + resolve(buffer); // 成功 + /** 存个日志 */ + if (temp !== 'true') { + const content_path = join( + __dirname, + '../../', + destPath, + `content.txt`, + ); + try { + await fs.appendFile( + content_path, + `type:${type}\nfont:${font}\ntext:${text}`, + ); + } catch (error) { + console.error(`写 ${content_path} 失败`); + } + } + } + }); + }); } }