|
1 | 1 | import fs from 'fs' |
2 | | -import { readFileSync, readdirSync, statSync } from 'fs' |
| 2 | +import { readFileSync, readdirSync, statSync, writeFileSync, unlinkSync } from 'fs' |
3 | 3 | import { join, dirname } from 'path' |
4 | 4 | import { fileURLToPath } from 'url' |
5 | | -import { execSync } from 'child_process' |
| 5 | +import { execSync, spawn } from 'child_process' |
| 6 | +import { tmpdir } from 'os' |
6 | 7 |
|
7 | 8 | const __filename = fileURLToPath(import.meta.url) |
8 | 9 | const __dirname = dirname(__filename) |
@@ -120,29 +121,89 @@ function generateFontSubset(characters) { |
120 | 121 | fs.mkdirSync(fontsOutputDir, { recursive: true }) |
121 | 122 | } |
122 | 123 |
|
123 | | - // 生成字体子集 |
124 | | - const command = `${pyftsubsetCmd} "${sourceFontPath}" \ |
125 | | - --text="${text}" \ |
126 | | - --flavor=woff2 \ |
127 | | - --output-file="${outputFontPath}" \ |
128 | | - --layout-features='*' \ |
129 | | - --glyph-names \ |
130 | | - --symbol-cmap \ |
131 | | - --legacy-cmap \ |
132 | | - --notdef-glyph \ |
133 | | - --notdef-outline \ |
134 | | - --recommended-glyphs` |
135 | | - |
| 124 | + // 创建临时文件来存储文本内容,避免 shell 引号转义问题 |
| 125 | + const tempTextFile = join(tmpdir(), `font-subset-text-${Date.now()}.txt`) |
136 | 126 | try { |
137 | | - execSync(command, { stdio: 'inherit' }) |
138 | | - console.log(`✅ 字体子集已生成: ${outputFontPath}`) |
| 127 | + writeFileSync(tempTextFile, text, 'utf-8') |
| 128 | + |
| 129 | + // 使用 --text-file 参数而不是 --text,避免 shell 引号问题 |
| 130 | + // 将命令拆分为数组,避免 shell 解析 |
| 131 | + const cmdParts = pyftsubsetCmd.split(' ') |
| 132 | + const args = [ |
| 133 | + ...cmdParts.slice(1), // 如果是 "python -m fontTools.subset",这里会包含 "-m", "fontTools.subset" |
| 134 | + sourceFontPath, |
| 135 | + '--text-file=' + tempTextFile, |
| 136 | + '--flavor=woff2', |
| 137 | + '--output-file=' + outputFontPath, |
| 138 | + '--layout-features=*', |
| 139 | + '--glyph-names', |
| 140 | + '--symbol-cmap', |
| 141 | + '--legacy-cmap', |
| 142 | + '--notdef-glyph', |
| 143 | + '--notdef-outline', |
| 144 | + '--recommended-glyphs' |
| 145 | + ] |
| 146 | + |
| 147 | + // 如果 pyftsubsetCmd 是单个命令(如 "pyftsubset"),则 cmdParts[0] 是命令本身 |
| 148 | + const command = cmdParts.length > 1 ? cmdParts[0] : pyftsubsetCmd |
| 149 | + |
| 150 | + // 使用 spawn 方式执行,避免 shell 解析问题 |
| 151 | + const child = spawn(command, args, { |
| 152 | + stdio: 'inherit', |
| 153 | + shell: false |
| 154 | + }) |
139 | 155 |
|
140 | | - // 显示文件大小 |
141 | | - const stats = fs.statSync(outputFontPath) |
142 | | - const sizeKB = (stats.size / 1024).toFixed(2) |
143 | | - const sizeMB = (stats.size / 1024 / 1024).toFixed(2) |
144 | | - console.log(`📦 文件大小: ${sizeKB} KB (${sizeMB} MB)`) |
| 156 | + return new Promise((resolve, reject) => { |
| 157 | + child.on('close', (code) => { |
| 158 | + // 清理临时文件 |
| 159 | + try { |
| 160 | + if (fs.existsSync(tempTextFile)) { |
| 161 | + unlinkSync(tempTextFile) |
| 162 | + } |
| 163 | + } catch (e) { |
| 164 | + // 忽略清理错误 |
| 165 | + } |
| 166 | + |
| 167 | + if (code !== 0) { |
| 168 | + console.error(`❌ 生成字体子集失败,退出码: ${code}`) |
| 169 | + process.exit(1) |
| 170 | + } |
| 171 | + |
| 172 | + console.log(`✅ 字体子集已生成: ${outputFontPath}`) |
| 173 | + |
| 174 | + // 显示文件大小 |
| 175 | + const stats = fs.statSync(outputFontPath) |
| 176 | + const sizeKB = (stats.size / 1024).toFixed(2) |
| 177 | + const sizeMB = (stats.size / 1024 / 1024).toFixed(2) |
| 178 | + console.log(`📦 文件大小: ${sizeKB} KB (${sizeMB} MB)`) |
| 179 | + |
| 180 | + resolve() |
| 181 | + }) |
| 182 | + |
| 183 | + child.on('error', (error) => { |
| 184 | + // 清理临时文件 |
| 185 | + try { |
| 186 | + if (fs.existsSync(tempTextFile)) { |
| 187 | + unlinkSync(tempTextFile) |
| 188 | + } |
| 189 | + } catch (e) { |
| 190 | + // 忽略清理错误 |
| 191 | + } |
| 192 | + |
| 193 | + console.error('❌ 生成字体子集失败:', error.message) |
| 194 | + reject(error) |
| 195 | + }) |
| 196 | + }) |
145 | 197 | } catch (error) { |
| 198 | + // 清理临时文件 |
| 199 | + try { |
| 200 | + if (fs.existsSync(tempTextFile)) { |
| 201 | + unlinkSync(tempTextFile) |
| 202 | + } |
| 203 | + } catch (e) { |
| 204 | + // 忽略清理错误 |
| 205 | + } |
| 206 | + |
146 | 207 | console.error('❌ 生成字体子集失败:', error.message) |
147 | 208 | process.exit(1) |
148 | 209 | } |
@@ -196,8 +257,8 @@ async function main() { |
196 | 257 | console.log(` - HTML 文件数: ${htmlFiles.length}`) |
197 | 258 | console.log(` - 提取字符数: ${allCharacters.size}`) |
198 | 259 |
|
199 | | - // 生成字体子集 |
200 | | - generateFontSubset(allCharacters) |
| 260 | + // 生成字体子集(现在是异步的) |
| 261 | + await generateFontSubset(allCharacters) |
201 | 262 |
|
202 | 263 | console.log('\n✨ 字体子集化完成!') |
203 | 264 | console.log('💡 字体文件已生成,可以直接部署') |
|
0 commit comments