foliko 1.1.7 → 1.1.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/.agent/agents/poster-expert.md +228 -0
- package/.agent/data/default.json +10 -0
- package/.agent/data/plugins-state.json +1 -1
- package/.agent/mcp_config.json +0 -6
- package/.agent/plugins/test-plugin.py +15 -0
- package/.agent/plugins/test_nested_plugin.py +85 -0
- package/.agent/sessions/cli_default.json +83 -803
- package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +189 -0
- package/.claude/settings.local.json +8 -1
- package/cli/src/ui/chat-ui.js +32 -21
- package/foliko_poster.png +0 -0
- package/output/business_poster_final.png +0 -0
- package/output/emoji_test_v2.png +0 -0
- package/output/foliko-ai-launch-poster.png +0 -0
- package/output/foliko-poster.png +0 -0
- package/output/muji_style_poster.png +0 -0
- package/output/new_product_launch.png +0 -0
- package/output/news_poster_625yi_subsidy.png +0 -0
- package/output/tech_future_2026.png +0 -0
- package/output/tech_future_poster.png +0 -0
- package/package.json +2 -2
- package/plugins/default-plugins.js +60 -7
- package/plugins/extension-executor-plugin.js +32 -22
- package/plugins/feishu-plugin.js +2 -2
- package/plugins/python-plugin-loader.js +461 -40
- package/plugins/python-plugin-loader.js.bak +856 -0
- package/plugins/subagent-plugin.js +0 -121
- package/plugins/telegram-plugin.js +2 -2
- package/plugins/weixin-plugin.js +40 -31
- package/poster.png +0 -0
- package/skills/foliko-dev/SKILL.md +6 -0
- package/skills/python-plugin-dev/SKILL.md +124 -2
- package/src/core/agent-chat.js +220 -281
- package/src/core/agent.js +127 -29
- package/src/executors/mcp-executor.js +282 -26
- package/src/utils/plugin-helpers.js +22 -4
- package/system.md +362 -109
- package/test-scan.js +18 -0
- package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +0 -15
- package/.agent/memory/feedback/mnt88ja3-al4fuy.md +0 -9
- package/.agent/plugins/poster-plugin/README.md +0 -304
- package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
- package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts/SegoeUI Emoji.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
- package/.agent/plugins/poster-plugin/index.js +0 -13
- package/.agent/plugins/poster-plugin/package.json +0 -29
- package/.agent/plugins/poster-plugin/src/canvas.js +0 -224
- package/.agent/plugins/poster-plugin/src/components/arrow.js +0 -84
- package/.agent/plugins/poster-plugin/src/components/avatar.js +0 -71
- package/.agent/plugins/poster-plugin/src/components/badge.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/barcode.js +0 -120
- package/.agent/plugins/poster-plugin/src/components/bubble.js +0 -153
- package/.agent/plugins/poster-plugin/src/components/button.js +0 -124
- package/.agent/plugins/poster-plugin/src/components/card.js +0 -88
- package/.agent/plugins/poster-plugin/src/components/chart.js +0 -127
- package/.agent/plugins/poster-plugin/src/components/chip.js +0 -88
- package/.agent/plugins/poster-plugin/src/components/columns.js +0 -107
- package/.agent/plugins/poster-plugin/src/components/cta.js +0 -87
- package/.agent/plugins/poster-plugin/src/components/divider.js +0 -55
- package/.agent/plugins/poster-plugin/src/components/feature.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/featureGrid.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/frame.js +0 -230
- package/.agent/plugins/poster-plugin/src/components/grid.js +0 -118
- package/.agent/plugins/poster-plugin/src/components/highlightText.js +0 -144
- package/.agent/plugins/poster-plugin/src/components/icon.js +0 -94
- package/.agent/plugins/poster-plugin/src/components/imageFrame.js +0 -155
- package/.agent/plugins/poster-plugin/src/components/index.js +0 -81
- package/.agent/plugins/poster-plugin/src/components/listItem.js +0 -147
- package/.agent/plugins/poster-plugin/src/components/notification.js +0 -123
- package/.agent/plugins/poster-plugin/src/components/progress.js +0 -79
- package/.agent/plugins/poster-plugin/src/components/progressCircle.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/qrcode.js +0 -74
- package/.agent/plugins/poster-plugin/src/components/quote.js +0 -97
- package/.agent/plugins/poster-plugin/src/components/rating.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/ribbon.js +0 -193
- package/.agent/plugins/poster-plugin/src/components/seal.js +0 -146
- package/.agent/plugins/poster-plugin/src/components/star.js +0 -70
- package/.agent/plugins/poster-plugin/src/components/statCard.js +0 -105
- package/.agent/plugins/poster-plugin/src/components/stepper.js +0 -118
- package/.agent/plugins/poster-plugin/src/components/table.js +0 -167
- package/.agent/plugins/poster-plugin/src/components/tagCloud.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/timeline.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/watermark.js +0 -52
- package/.agent/plugins/poster-plugin/src/composer.js +0 -2146
- package/.agent/plugins/poster-plugin/src/elements/artText.js +0 -60
- package/.agent/plugins/poster-plugin/src/elements/background.js +0 -84
- package/.agent/plugins/poster-plugin/src/elements/circle.js +0 -31
- package/.agent/plugins/poster-plugin/src/elements/image.js +0 -28
- package/.agent/plugins/poster-plugin/src/elements/index.js +0 -28
- package/.agent/plugins/poster-plugin/src/elements/line.js +0 -23
- package/.agent/plugins/poster-plugin/src/elements/polygon.js +0 -63
- package/.agent/plugins/poster-plugin/src/elements/rectangle.js +0 -32
- package/.agent/plugins/poster-plugin/src/elements/richText.js +0 -230
- package/.agent/plugins/poster-plugin/src/elements/svg.js +0 -108
- package/.agent/plugins/poster-plugin/src/elements/text.js +0 -109
- package/.agent/plugins/poster-plugin/src/fonts.js +0 -243
- package/.agent/plugins/poster-plugin/src/index.js +0 -2081
- package/.agent/plugins/poster-plugin/src/presets.js +0 -36
- package/.agent/plugins/poster-plugin/src/templates/business.js +0 -60
- package/.agent/plugins/poster-plugin/src/templates/gradient.js +0 -64
- package/.agent/plugins/poster-plugin/src/templates/index.js +0 -43
- package/.agent/plugins/poster-plugin/src/templates/modern.js +0 -69
- package/.agent/plugins/poster-plugin/src/templates/simple.js +0 -58
- package/.agent/plugins/poster-plugin/src/templates/social.js +0 -62
- package/.agent/plugins/poster-plugin/src/templates/tech.js +0 -84
- package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +0 -84
- package/.agent/plugins/poster-plugin/test-background.svg +0 -1
- package/.agent/plugins/poster-plugin/test-full-poster.svg +0 -2
- package/.agent/plugins/poster-plugin/test-image.png +0 -0
- package/.agent/plugins/poster-plugin/yarn.lock +0 -1007
- package/output/zen_silence.png +0 -0
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 文字元素
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const paper = require('paper')
|
|
6
|
-
const { validateFont, getDefaultFont, getRegisteredFonts } = require('../fonts')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 检测文本是否包含 emoji
|
|
10
|
-
*/
|
|
11
|
-
function containsEmoji(text) {
|
|
12
|
-
if (!text || typeof text !== 'string') return false
|
|
13
|
-
|
|
14
|
-
// Emoji Unicode 范围
|
|
15
|
-
const emojiRanges = [
|
|
16
|
-
/\u{1F600}-\u{1F64F}/u, // 表情符号
|
|
17
|
-
/\u{1F300}-\u{1F5FF}/u, // 符号和图片
|
|
18
|
-
/\u{1F680}-\u{1F6FF}/u, // 交通和地图
|
|
19
|
-
/\u{1F700}-\u{1F77F}/u, // 字母符号
|
|
20
|
-
/\u{1F780}-\u{1F7FF}/u, // 几何符号扩展
|
|
21
|
-
/\u{1F800}-\u{1F8FF}/u, // 箭头补充
|
|
22
|
-
/\u{1F900}-\u{1F9FF}/u, // 表情符号补充
|
|
23
|
-
/\u{1FA00}-\u{1FA6F}/u, // 棋牌符号
|
|
24
|
-
/\u{1FA70}-\u{1FAFF}/u, // 装饰符号
|
|
25
|
-
/\u{2600}-\u{26FF}/u, // 杂项符号
|
|
26
|
-
/\u{2700}-\u{27BF}/u, // 装饰符号
|
|
27
|
-
/[\u{1F000}-\u{1F02F}]/u, // 麻将牌
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
for (const range of emojiRanges) {
|
|
31
|
-
if (range.test(text)) {
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 简单检测:包含常见 emoji 字符
|
|
37
|
-
const simpleEmoji = /[\u{1F300}-\u{1F9FF}]/u
|
|
38
|
-
return simpleEmoji.test(text)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 获取适合的字体(考虑 emoji)
|
|
43
|
-
*/
|
|
44
|
-
function getFontForText(requestedFont, text) {
|
|
45
|
-
const baseFont = validateFont(requestedFont) || getDefaultFont()
|
|
46
|
-
|
|
47
|
-
// 如果文本包含 emoji,尝试使用支持 emoji 的字体
|
|
48
|
-
if (containsEmoji(text)) {
|
|
49
|
-
const registeredFonts = getRegisteredFonts()
|
|
50
|
-
|
|
51
|
-
// 优先查找专门的 emoji 字体(多种命名方式)
|
|
52
|
-
const emojiFont = registeredFonts.find(f => {
|
|
53
|
-
if (!f) return false
|
|
54
|
-
const lower = f.toLowerCase()
|
|
55
|
-
return (
|
|
56
|
-
lower.includes('color emoji') || // Noto Color Emoji, Apple Color Emoji
|
|
57
|
-
lower.includes('noto emoji') || // Noto Emoji
|
|
58
|
-
lower.includes('segoe ui emoji') || // Windows Segoe UI Emoji
|
|
59
|
-
lower.includes('segoe ui symbol') || // Windows Segoe UI Symbol
|
|
60
|
-
lower.includes('segui') || // Windows Segoe UI 开头
|
|
61
|
-
lower.includes('symbola') || // Symbola
|
|
62
|
-
lower.includes('emoji') // 任何包含 emoji 的字体
|
|
63
|
-
)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
if (emojiFont) {
|
|
67
|
-
console.log(`[poster] 检测到 emoji,使用字体: ${emojiFont}`)
|
|
68
|
-
return emojiFont
|
|
69
|
-
} else {
|
|
70
|
-
console.log(`[poster] 检测到 emoji,但未找到专用 emoji 字体,已注册字体: ${registeredFonts.join(', ')}`)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return baseFont
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 添加文字
|
|
79
|
-
*/
|
|
80
|
-
function addText(project, args) {
|
|
81
|
-
const {
|
|
82
|
-
text,
|
|
83
|
-
x, y,
|
|
84
|
-
fontSize, fontFamily, color,
|
|
85
|
-
align,
|
|
86
|
-
shadow,
|
|
87
|
-
} = args
|
|
88
|
-
|
|
89
|
-
const font = getFontForText(fontFamily, text)
|
|
90
|
-
|
|
91
|
-
const textItem = new paper.PointText({
|
|
92
|
-
point: [x, y],
|
|
93
|
-
content: text,
|
|
94
|
-
fontSize: fontSize || 48,
|
|
95
|
-
fontFamily: font,
|
|
96
|
-
fillColor: new paper.Color(color || '#ffffff'),
|
|
97
|
-
justification: align || 'left',
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
if (shadow) {
|
|
101
|
-
textItem.shadowColor = new paper.Color(shadow.color)
|
|
102
|
-
textItem.shadowBlur = shadow.blur || 5
|
|
103
|
-
textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return { success: true, id: textItem.id, type: 'text' }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
module.exports = addText
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 字体管理模块
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const path = require('path')
|
|
6
|
-
const fs = require('fs')
|
|
7
|
-
const { registerFont: registerFontFn } = require('canvas')
|
|
8
|
-
|
|
9
|
-
// 已注册的字体
|
|
10
|
-
const registeredFonts = new Map()
|
|
11
|
-
|
|
12
|
-
// Emoji 字体名称映射(用于统一识别)
|
|
13
|
-
const emojiFontMappings = {
|
|
14
|
-
'NotoColorEmoji': 'Noto Color Emoji',
|
|
15
|
-
'NotoColorEmoji-Regular': 'Noto Color Emoji',
|
|
16
|
-
'Noto Emoji': 'Noto Emoji',
|
|
17
|
-
'NotoEmoji-Regular': 'Noto Emoji',
|
|
18
|
-
'Apple Color Emoji': 'Apple Color Emoji',
|
|
19
|
-
'Segoe UI Emoji': 'Segoe UI Emoji',
|
|
20
|
-
'SegoeUI Emoji': 'Segoe UI Emoji',
|
|
21
|
-
'Symbola': 'Symbola',
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// 系统字体路径
|
|
25
|
-
const systemFonts = [
|
|
26
|
-
// Windows 字体
|
|
27
|
-
{ path: 'C:\\Windows\\Fonts\\msyh.ttc', family: 'Microsoft YaHei' },
|
|
28
|
-
{ path: 'C:\\Windows\\Fonts\\msyhbd.ttc', family: 'Microsoft YaHei Bold', weight: 'bold' },
|
|
29
|
-
{ path: 'C:\\Windows\\Fonts\\simhei.ttf', family: 'SimHei' },
|
|
30
|
-
{ path: 'C:\\Windows\\Fonts\\simsun.ttc', family: 'SimSun' },
|
|
31
|
-
{ path: 'C:\\Windows\\Fonts\\Arial.ttf', family: 'Arial' },
|
|
32
|
-
{ path: 'C:\\Windows\\Fonts\\Times New Roman.ttf', family: 'Times New Roman' },
|
|
33
|
-
{ path: 'C:\\Windows\\Fonts\\Consolas.ttf', family: 'Consolas' },
|
|
34
|
-
{ path: 'C:\\Windows\\Fonts\\Georgia.ttf', family: 'Georgia' },
|
|
35
|
-
// Windows Emoji 字体
|
|
36
|
-
{ path: 'C:\\Windows\\Fonts\\seguiemj.ttf', family: 'Segoe UI Emoji' },
|
|
37
|
-
{ path: 'C:\\Windows\\Fonts\\seguisym.ttf', family: 'Segoe UI Symbol' },
|
|
38
|
-
{ path: 'C:\\Windows\\Fonts\\seguisb.ttf', family: 'Segoe UI Symbol' },
|
|
39
|
-
{ path: 'C:\\Windows\\Fonts\\EmojiOne Color.ttf', family: 'EmojiOne Color' },
|
|
40
|
-
// Linux emoji 字体(扩展搜索路径)
|
|
41
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
42
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoEmoji-Regular.ttf', family: 'Noto Emoji' },
|
|
43
|
-
{ path: '/usr/share/fonts/opentype/noto/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
44
|
-
{ path: '/usr/share/fonts/google-noto-cursive/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
45
|
-
{ path: '/usr/share/fonts/google-noto-emoji/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
46
|
-
{ path: '/usr/share/fonts/noto-fonts/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
47
|
-
{ path: '/usr/share/fonts/TTF/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
48
|
-
{ path: '/usr/share/fonts/truetype/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
49
|
-
{ path: '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', family: 'DejaVu Sans' },
|
|
50
|
-
{ path: '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', family: 'Liberation Sans' },
|
|
51
|
-
{ path: '/usr/share/fonts/truetype/freefont/FreeSans.ttf', family: 'FreeSans' },
|
|
52
|
-
{ path: '/usr/share/fonts/TTF/NotoSans-Regular.ttf', family: 'Noto Sans' },
|
|
53
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf', family: 'Noto Sans' },
|
|
54
|
-
// macOS 字体
|
|
55
|
-
{ path: '/System/Library/Fonts/Apple Color Emoji.ttc', family: 'Apple Color Emoji' },
|
|
56
|
-
{ path: '/System/Library/Fonts/Supplemental/Symbola.ttf', family: 'Symbola' },
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
// 默认字体
|
|
60
|
-
let defaultFontFamily = '微软雅黑'
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 注册字体文件
|
|
64
|
-
*/
|
|
65
|
-
function registerFontFile(fontPath, fontFamily, options = {}) {
|
|
66
|
-
if (registeredFonts.has(fontFamily)) {
|
|
67
|
-
return true
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
if (!fs.existsSync(fontPath)) {
|
|
72
|
-
return false
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
registerFontFn(fontPath, {
|
|
76
|
-
family: fontFamily,
|
|
77
|
-
weight: options.weight || 'normal',
|
|
78
|
-
style: options.style || 'normal',
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
registeredFonts.set(fontFamily, {
|
|
82
|
-
path: fontPath,
|
|
83
|
-
...options,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
return true
|
|
87
|
-
} catch (e) {
|
|
88
|
-
console.log(`[poster] 注册字体失败: ${fontFamily}`, e.message)
|
|
89
|
-
return false
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 判断是否为 Emoji 字体
|
|
95
|
-
*/
|
|
96
|
-
function isEmojiFont(fontName) {
|
|
97
|
-
if (!fontName) return false
|
|
98
|
-
const lower = fontName.toLowerCase()
|
|
99
|
-
return (
|
|
100
|
-
lower.includes('emoji') ||
|
|
101
|
-
lower.includes('color') ||
|
|
102
|
-
lower.includes('symbola')
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 初始化字体
|
|
108
|
-
*/
|
|
109
|
-
function initFonts() {
|
|
110
|
-
// 插件字体目录
|
|
111
|
-
const pluginFontsDir = path.join(__dirname, '..', 'fonts')
|
|
112
|
-
|
|
113
|
-
// 优先加载插件自带的字体
|
|
114
|
-
if (fs.existsSync(pluginFontsDir)) {
|
|
115
|
-
const fontFiles = fs.readdirSync(pluginFontsDir)
|
|
116
|
-
for (const file of fontFiles) {
|
|
117
|
-
if (!file.endsWith('.ttf') && !file.endsWith('.otf') && !file.endsWith('.ttc')) {
|
|
118
|
-
continue
|
|
119
|
-
}
|
|
120
|
-
const fontPath = path.join(pluginFontsDir, file)
|
|
121
|
-
const fontName = path.basename(file, path.extname(file))
|
|
122
|
-
|
|
123
|
-
// 检查是否为 Emoji 字体,如果是则使用标准名称注册
|
|
124
|
-
let finalFontName = fontName
|
|
125
|
-
if (isEmojiFont(fontName)) {
|
|
126
|
-
// 尝试映射到标准名称
|
|
127
|
-
finalFontName = emojiFontMappings[fontName] || 'Noto Color Emoji'
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// 尝试注册
|
|
131
|
-
if (registerFontFile(fontPath, finalFontName)) {
|
|
132
|
-
if (isEmojiFont(finalFontName)) {
|
|
133
|
-
console.log(`[poster] 已注册插件 Emoji 字体: ${finalFontName}`)
|
|
134
|
-
} else {
|
|
135
|
-
// 微软雅黑设为默认字体
|
|
136
|
-
if (fontName.includes('微软雅黑') && !fontName.includes('粗体')) {
|
|
137
|
-
defaultFontFamily = finalFontName
|
|
138
|
-
console.log(`[poster] 已注册插件字体(设为默认): ${finalFontName}`)
|
|
139
|
-
} else if (!defaultFontFamily || defaultFontFamily === 'sans-serif') {
|
|
140
|
-
defaultFontFamily = finalFontName
|
|
141
|
-
console.log(`[poster] 已注册插件字体: ${finalFontName}`)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 如果没有注册到插件字体,再尝试系统字体
|
|
149
|
-
if (!defaultFontFamily || defaultFontFamily === 'sans-serif') {
|
|
150
|
-
for (const font of systemFonts) {
|
|
151
|
-
if (registerFontFile(font.path, font.family, { weight: font.weight })) {
|
|
152
|
-
if (font.weight !== 'bold') {
|
|
153
|
-
defaultFontFamily = font.family
|
|
154
|
-
console.log(`[poster] 已注册系统字体: ${font.family}`)
|
|
155
|
-
break
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 确保有默认字体
|
|
162
|
-
if (!registeredFonts.has(defaultFontFamily)) {
|
|
163
|
-
registeredFonts.set('sans-serif', { path: null })
|
|
164
|
-
console.log('[poster] 使用默认字体: sans-serif')
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 注册系统 emoji 字体(用于支持 emoji 渲染)
|
|
168
|
-
const emojiFonts = [
|
|
169
|
-
// Windows Emoji 字体(优先)
|
|
170
|
-
{ path: 'C:\\Windows\\Fonts\\seguiemj.ttf', family: 'Segoe UI Emoji' },
|
|
171
|
-
{ path: 'C:\\Windows\\Fonts\\seguisym.ttf', family: 'Segoe UI Symbol' },
|
|
172
|
-
{ path: 'C:\\Windows\\Fonts\\seguisb.ttf', family: 'Segoe UI Symbol' },
|
|
173
|
-
// Linux
|
|
174
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
175
|
-
{ path: '/usr/share/fonts/opentype/noto/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
176
|
-
{ path: '/usr/share/fonts/google-noto-emoji/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
177
|
-
{ path: '/usr/share/fonts/google-noto-cursive/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
178
|
-
{ path: '/usr/share/fonts/noto-fonts/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
179
|
-
{ path: '/usr/share/fonts/TTF/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
180
|
-
{ path: '/usr/share/fonts/truetype/NotoColorEmoji.ttf', family: 'Noto Color Emoji' },
|
|
181
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoEmoji-Regular.ttf', family: 'Noto Emoji' },
|
|
182
|
-
{ path: '/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf', family: 'Noto Sans' },
|
|
183
|
-
{ path: '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', family: 'DejaVu Sans' },
|
|
184
|
-
{ path: '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', family: 'Liberation Sans' },
|
|
185
|
-
{ path: '/usr/share/fonts/truetype/freefont/FreeSans.ttf', family: 'FreeSans' },
|
|
186
|
-
// macOS
|
|
187
|
-
{ path: '/System/Library/Fonts/Apple Color Emoji.ttc', family: 'Apple Color Emoji' },
|
|
188
|
-
{ path: '/System/Library/Fonts/Supplemental/Symbola.ttf', family: 'Symbola' },
|
|
189
|
-
]
|
|
190
|
-
|
|
191
|
-
for (const font of emojiFonts) {
|
|
192
|
-
if (registerFontFile(font.path, font.family)) {
|
|
193
|
-
// 只在还没注册过 Noto Color Emoji 时打印
|
|
194
|
-
if (!registeredFonts.has('Noto Color Emoji') || font.family === 'Noto Color Emoji') {
|
|
195
|
-
console.log(`[poster] 已注册 emoji 字体: ${font.family}`)
|
|
196
|
-
}
|
|
197
|
-
// 一旦找到 Noto Color Emoji 就停止
|
|
198
|
-
if (font.family === 'Noto Color Emoji' && registeredFonts.has('Noto Color Emoji')) {
|
|
199
|
-
break
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 验证字体是否可用
|
|
207
|
-
*/
|
|
208
|
-
function validateFont(fontFamily) {
|
|
209
|
-
if (!fontFamily) return defaultFontFamily
|
|
210
|
-
if (registeredFonts.has(fontFamily)) return fontFamily
|
|
211
|
-
|
|
212
|
-
const lower = fontFamily.toLowerCase()
|
|
213
|
-
for (const [name] of registeredFonts) {
|
|
214
|
-
if (name.toLowerCase() === lower) return name
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
//console.log(`[poster] 字体 "${fontFamily}" 未找到,使用默认字体: ${defaultFontFamily}`)
|
|
218
|
-
return defaultFontFamily
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* 获取已注册字体列表
|
|
223
|
-
*/
|
|
224
|
-
function getRegisteredFonts() {
|
|
225
|
-
return Array.from(registeredFonts.keys())
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* 获取默认字体
|
|
230
|
-
*/
|
|
231
|
-
function getDefaultFont() {
|
|
232
|
-
return defaultFontFamily
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// 初始化
|
|
236
|
-
initFonts()
|
|
237
|
-
|
|
238
|
-
module.exports = {
|
|
239
|
-
registerFontFile,
|
|
240
|
-
validateFont,
|
|
241
|
-
getRegisteredFonts,
|
|
242
|
-
getDefaultFont,
|
|
243
|
-
}
|