@xingwangzhe/stalux 1.4.6 → 1.5.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/stalux",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "A powerful, modern Astro blog theme \u2014 use as template or install as plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"pagefind": "^1.5.2",
|
|
92
92
|
"photoswipe": "^5.4.4",
|
|
93
93
|
"simple-icons": "^16.27.1",
|
|
94
|
-
"
|
|
94
|
+
"taetype": "0.1.3",
|
|
95
95
|
"temml": "^0.13.3"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
@@ -12,7 +12,8 @@ import { resolve, join, dirname } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
|
|
14
14
|
import type { AstroIntegrationLogger } from "astro";
|
|
15
|
-
import
|
|
15
|
+
import * as taetype from "taetype";
|
|
16
|
+
import { register_font_raw, get_glyph_ids, subset_font_full } from "taetype";
|
|
16
17
|
|
|
17
18
|
const FONT_INPUT = "src/assets/fonts/LXGWWenKai-Regular.ttf";
|
|
18
19
|
const CONTENT_ROOT = "stalux";
|
|
@@ -126,17 +127,22 @@ async function generateSubsetOnDemand(
|
|
|
126
127
|
const fontBuffer = readFileSync(fontPath);
|
|
127
128
|
mkdirSync(outDir, { recursive: true });
|
|
128
129
|
|
|
129
|
-
// Generate subset
|
|
130
|
+
// Generate subset TTF using taetype
|
|
130
131
|
const charsStr = [...charSet].sort().join("");
|
|
132
|
+
try { register_font_raw("stalux", fontBuffer); } catch { /* already registered */ }
|
|
133
|
+
const glyphs = get_glyph_ids(charsStr, "stalux", "normal", 400);
|
|
134
|
+
if (glyphs.length === 0) return null;
|
|
135
|
+
const subsetResult = subset_font_full("stalux", "normal", 400, 0, glyphs);
|
|
131
136
|
const hash = [...charsStr].slice(0, 12).join("");
|
|
132
|
-
const filename = `subset-${subsetId}-${hash}.
|
|
137
|
+
const filename = `subset-${subsetId}-${hash}.ttf`;
|
|
133
138
|
const outPath = join(outDir, filename);
|
|
134
139
|
|
|
135
140
|
if (!existsSync(outPath)) {
|
|
136
141
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
writeFileSync(outPath, subsetResult.fontBytes);
|
|
143
|
+
logger.info(
|
|
144
|
+
` on-demand subset: ${(subsetResult.fontBytes.length / 1024).toFixed(1)} KB → ${filename}`,
|
|
145
|
+
);
|
|
140
146
|
} catch {
|
|
141
147
|
return null;
|
|
142
148
|
}
|
|
@@ -144,7 +150,7 @@ async function generateSubsetOnDemand(
|
|
|
144
150
|
|
|
145
151
|
return `@font-face {
|
|
146
152
|
font-family: "LXGW WenKai Subset";
|
|
147
|
-
src: url("/fonts/${filename}") format("
|
|
153
|
+
src: url("/fonts/${filename}") format("truetype");
|
|
148
154
|
font-display: swap;
|
|
149
155
|
}`;
|
|
150
156
|
}
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Output layout (under public/fonts/):
|
|
9
9
|
* fonts/
|
|
10
|
-
* common.
|
|
11
|
-
* post-{abbrlink}.
|
|
12
|
-
* about.
|
|
13
|
-
* words.
|
|
10
|
+
* common.ttf — UI text subset (ASCII, nav, i18n)
|
|
11
|
+
* post-{abbrlink}.ttf — per-post subset
|
|
12
|
+
* about.ttf — about page subset
|
|
13
|
+
* words.ttf — combined words subset
|
|
14
14
|
* manifest.json — route → subset file mapping
|
|
15
15
|
* subset.css — global @font-face declarations
|
|
16
16
|
*/
|
|
@@ -21,8 +21,9 @@ import { resolve, basename, extname, join, dirname } from "node:path";
|
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
|
|
23
23
|
import type { AstroIntegrationLogger } from "astro";
|
|
24
|
-
// subset-font
|
|
25
|
-
import
|
|
24
|
+
// taetype 是 Rust 原生字体引擎,比 subset-font (WASM) 快 2-3 倍
|
|
25
|
+
import * as taetype from "taetype";
|
|
26
|
+
import { register_font_raw, get_glyph_ids, subset_font_full } from "taetype";
|
|
26
27
|
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
28
29
|
// Configuration
|
|
@@ -263,6 +264,14 @@ export async function runFontSubsetting(
|
|
|
263
264
|
}
|
|
264
265
|
const fontBuffer = readFileSync(fontPath);
|
|
265
266
|
|
|
267
|
+
// Register font with taetype for fast Rust subsetting
|
|
268
|
+
try {
|
|
269
|
+
register_font_raw("stalux", fontBuffer);
|
|
270
|
+
} catch (e) {
|
|
271
|
+
logger.warn(`Font registration failed: ${e}, skipping subsetting`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
266
275
|
// Clean old generated font files to avoid stale cached references
|
|
267
276
|
const outDir = resolve(projectRoot, FONT_OUT_DIR);
|
|
268
277
|
if (existsSync(outDir)) {
|
|
@@ -298,26 +307,33 @@ export async function runFontSubsetting(
|
|
|
298
307
|
});
|
|
299
308
|
}
|
|
300
309
|
|
|
301
|
-
// 5. Deduplicate and generate subsets
|
|
310
|
+
// 5. Deduplicate and generate subsets using taetype (Rust native, fast)
|
|
302
311
|
mkdirSync(outDir, { recursive: true });
|
|
303
312
|
|
|
304
|
-
//
|
|
305
|
-
|
|
313
|
+
// Helper: subset using taetype
|
|
314
|
+
function doSubset(chars: string): Uint8Array {
|
|
315
|
+
const glyphs = get_glyph_ids(chars, "stalux", "normal", 400);
|
|
316
|
+
if (glyphs.length === 0) return new Uint8Array(0);
|
|
317
|
+
const result = subset_font_full("stalux", "normal", 400, 0, glyphs);
|
|
318
|
+
return result.fontBytes;
|
|
319
|
+
}
|
|
306
320
|
|
|
307
321
|
// Generate common subset
|
|
308
|
-
const commonFilename = `common-${commonHash}.
|
|
322
|
+
const commonFilename = `common-${commonHash}.ttf`;
|
|
309
323
|
const commonOutPath = join(outDir, commonFilename);
|
|
310
324
|
if (!existsSync(commonOutPath)) {
|
|
311
|
-
const data =
|
|
312
|
-
|
|
313
|
-
|
|
325
|
+
const data = doSubset(commonChars);
|
|
326
|
+
if (data.length > 0) {
|
|
327
|
+
writeFileSync(commonOutPath, data);
|
|
328
|
+
logger.info(` common subset: ${(data.length / 1024).toFixed(1)} KB → ${commonFilename}`);
|
|
329
|
+
}
|
|
314
330
|
}
|
|
315
331
|
|
|
316
332
|
// Generate common CSS
|
|
317
333
|
const commonCSS = `/* Auto-generated common font subset */
|
|
318
334
|
@font-face {
|
|
319
335
|
font-family: "LXGW WenKai Subset";
|
|
320
|
-
src: url("/fonts/${commonFilename}") format("
|
|
336
|
+
src: url("/fonts/${commonFilename}") format("truetype");
|
|
321
337
|
font-display: swap;
|
|
322
338
|
}
|
|
323
339
|
`;
|
|
@@ -330,39 +346,38 @@ export async function runFontSubsetting(
|
|
|
330
346
|
let subsetCount = 0;
|
|
331
347
|
const cssGenerated = new Set<string>();
|
|
332
348
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
});
|
|
350
|
-
writeFileSync(outPath, data);
|
|
349
|
+
if (!skipPerRoute) {
|
|
350
|
+
for (const [hash, routes] of groups) {
|
|
351
|
+
const filename = `subset-${hash}.ttf`;
|
|
352
|
+
const outPath = join(outDir, filename);
|
|
353
|
+
if (!existsSync(outPath)) {
|
|
354
|
+
const route = routeSets.find((r) => r.hash === hash);
|
|
355
|
+
const chars = route?.chars ?? "";
|
|
356
|
+
const uniqueChars = chars
|
|
357
|
+
.split("")
|
|
358
|
+
.filter((ch) => !commonChars.includes(ch))
|
|
359
|
+
.join("");
|
|
360
|
+
if (uniqueChars.length > 0) {
|
|
361
|
+
const glyphs = get_glyph_ids(uniqueChars, "stalux", "normal", 400);
|
|
362
|
+
if (glyphs.length > 0) {
|
|
363
|
+
const result = subset_font_full("stalux", "normal", 400, 0, glyphs);
|
|
364
|
+
writeFileSync(outPath, result.fontBytes);
|
|
351
365
|
subsetCount++;
|
|
352
366
|
logger.info(
|
|
353
|
-
` subset ${subsetCount}: ${(
|
|
367
|
+
` subset ${subsetCount}: ${(result.fontBytes.length / 1024).toFixed(1)} KB → ${filename}`,
|
|
354
368
|
);
|
|
355
369
|
}
|
|
356
370
|
}
|
|
371
|
+
}
|
|
357
372
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
373
|
+
// Generate per-route CSS (one per unique hash, references all routes with this hash)
|
|
374
|
+
if (!cssGenerated.has(hash)) {
|
|
375
|
+
cssGenerated.add(hash);
|
|
376
|
+
for (const route of routes) {
|
|
377
|
+
const cssContent = `/* Auto-generated font subset for route: ${route} */
|
|
363
378
|
@font-face {
|
|
364
379
|
font-family: "LXGW WenKai Subset";
|
|
365
|
-
src: url("/fonts/${filename}") format("
|
|
380
|
+
src: url("/fonts/${filename}") format("truetype");
|
|
366
381
|
font-display: swap;
|
|
367
382
|
}
|
|
368
383
|
`;
|