koishi-plugin-group-analysis-mx 1.2.8 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/service/renderer.js +21 -24
- package/package.json +1 -1
package/lib/service/renderer.js
CHANGED
|
@@ -73,45 +73,42 @@ class RendererService extends koishi_1.Service {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
/** 往渲染 HTML 注入 emoji 字体 @font-face 与字体栈,避免 puppeteer 截图时 emoji 显示为方框/问号。 */
|
|
76
|
-
async injectEmojiFont(html) {
|
|
76
|
+
async injectEmojiFont(html, htmlDir) {
|
|
77
77
|
if (!html || typeof html !== 'string')
|
|
78
78
|
return html;
|
|
79
|
-
|
|
79
|
+
// 字体目录相对于当前临时 HTML 所在目录,供 @font-face 用相对路径加载(Chromium 最稳定)
|
|
80
|
+
const fontsAbs = path_1.default.resolve(this.templateDir, 'fonts');
|
|
81
|
+
let rel = 'fonts';
|
|
80
82
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.ctx.logger.warn('构建 emoji 字体 CSS 失败:', e);
|
|
83
|
+
rel = path_1.default.relative(path_1.default.resolve(htmlDir || this.templateDir), fontsAbs).replace(/\\/g, '/');
|
|
84
|
+
if (rel && !rel.startsWith('.'))
|
|
85
|
+
rel = './' + rel;
|
|
85
86
|
}
|
|
87
|
+
catch (e) { /* use default */ }
|
|
88
|
+
const emojiCss = this.buildEmojiFontCss(rel);
|
|
86
89
|
const style = `<style>${emojiCss}html,body{font-family:-apple-system,'Segoe UI','Segoe UI Emoji','NotoColorEmoji','Noto Sans SC','Noto Color Emoji','Apple Color Emoji','Microsoft YaHei','Hiragino Sans GB',sans-serif !important;}</style>`;
|
|
87
90
|
if (html.includes('</head>'))
|
|
88
91
|
return html.replace('</head>', style + '</head>');
|
|
89
92
|
return html;
|
|
90
93
|
}
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return _emojiFontCssCache;
|
|
95
|
-
const dir = __dirname + '/../../resources/fonts';
|
|
94
|
+
/** 生成引用磁盘上 emoji 字体分片的 @font-face(相对路径,Chromium 稳定加载)。 */
|
|
95
|
+
buildEmojiFontCss(relPath) {
|
|
96
|
+
const rel = relPath || 'fonts';
|
|
96
97
|
let css = '';
|
|
97
98
|
for (let i = 0; i < EMOJI_RANGES.length; i++) {
|
|
98
|
-
const file =
|
|
99
|
-
|
|
100
|
-
const buf = await fs_1.promises.readFile(file);
|
|
101
|
-
const b64 = buf.toString('base64');
|
|
102
|
-
css += `@font-face{font-family:'NotoColorEmoji';font-style:normal;font-weight:400;src:url(data:font/woff2;base64,${b64}) format('woff2');unicode-range:${EMOJI_RANGES[i]};}`;
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
this.ctx.logger.warn(`加载 emoji 字体分片 ${i} 失败: ${e}`);
|
|
106
|
-
}
|
|
99
|
+
const file = `${String(i).padStart(2, '0')}.woff2`;
|
|
100
|
+
css += `@font-face{font-family:'NotoColorEmoji';font-style:normal;font-weight:400;src:url('${rel}/${file}') format('woff2');unicode-range:${EMOJI_RANGES[i]};}`;
|
|
107
101
|
}
|
|
108
|
-
_emojiFontCssCache = css;
|
|
109
102
|
return css;
|
|
110
103
|
}
|
|
111
104
|
async init() {
|
|
112
105
|
const resourcesDir = __dirname + '/../../resources';
|
|
113
106
|
const templateDir = this.templateDir;
|
|
114
107
|
const skin = this.config.skin || 'md3';
|
|
108
|
+
// 拷贝内嵌的 emoji 字体到 data 目录,供 @font-face 以相对路径引用(Chromium 最稳定的加载方式)
|
|
109
|
+
const fontsDestDir = path_1.default.resolve(templateDir, 'fonts');
|
|
110
|
+
await fs_1.promises.mkdir(fontsDestDir, { recursive: true });
|
|
111
|
+
await fs_1.promises.cp(path_1.default.resolve(resourcesDir, 'fonts'), fontsDestDir, { recursive: true });
|
|
115
112
|
// 拷贝原项目皮肤模板(nunjucks 渲染用)
|
|
116
113
|
const originalSourceDir = path_1.default.resolve(resourcesDir, 'original');
|
|
117
114
|
const originalDestDir = path_1.default.resolve(templateDir, 'original');
|
|
@@ -238,7 +235,7 @@ class RendererService extends koishi_1.Service {
|
|
|
238
235
|
dynamicAvatarUrl: dynamicAvatarBase64
|
|
239
236
|
});
|
|
240
237
|
// 写入临时 HTML 文件
|
|
241
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml));
|
|
238
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml, path_1.default.dirname(outTemplateHtmlPath)));
|
|
242
239
|
this.ctx.logger.info('HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
|
|
243
240
|
const page = await this.ctx.puppeteer.page();
|
|
244
241
|
// 重新加载页面并使用 goto 访问本地文件
|
|
@@ -309,7 +306,7 @@ class RendererService extends koishi_1.Service {
|
|
|
309
306
|
const html = env.render('html_template.html', context);
|
|
310
307
|
const randomId = Math.random().toString(36).substring(2, 15);
|
|
311
308
|
const outTemplateHtmlPath = path_1.default.resolve(skinDir, `${randomId}.html`);
|
|
312
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(html));
|
|
309
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(html, path_1.default.dirname(outTemplateHtmlPath)));
|
|
313
310
|
this.ctx.logger.info(`原项目皮肤 ${skin} HTML 生成完成,正在调用 Puppeteer 渲染...`);
|
|
314
311
|
const page = await this.ctx.puppeteer.page();
|
|
315
312
|
await page.setViewport({ width: 1080, height: 1920, deviceScaleFactor: 2 });
|
|
@@ -525,7 +522,7 @@ class RendererService extends koishi_1.Service {
|
|
|
525
522
|
theme,
|
|
526
523
|
dynamicAvatarUrl: dynamicAvatarBase64
|
|
527
524
|
});
|
|
528
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml));
|
|
525
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml, path_1.default.dirname(outTemplateHtmlPath)));
|
|
529
526
|
this.ctx.logger.info('用户画像 HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
|
|
530
527
|
const page = await this.ctx.puppeteer.page();
|
|
531
528
|
// 重新加载页面并使用 goto 访问本地文件
|
package/package.json
CHANGED