// 出正式文件(2026-10-14 用户选定 C 圆形徽章,三端统一):
// ../交付/<端>/favicon.svg(字已转路径,不依赖字体)、favicon-16/32/48.png、favicon.ico(16/32/48 三档 BMP)、apple-touch-icon.png 180、icon-192.png、icon-512.png
// 字形来自 glyphs.json(glyphs.py 用 fontTools 从 PingFang SC Semibold / Inter SemiBold 抽出,1000 UPM,y 向下)。
// 用法:node deliver.mjs
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { chromium } from "playwright";
import { PNG } from "pngjs";
import { SYS, BLUE } from "./icons.mjs";
const here = path.dirname(fileURLToPath(import.meta.url));
const out = path.resolve(here, "../交付");
const G = JSON.parse(fs.readFileSync(path.join(here, "glyphs.json"), "utf8"));
// 和选型页 C 一致:圆底渐变、r=26 的 55% 白环、白字;汉字 36 号、拉丁 42 号。16px 专用版去掉白环、字放大(汉字 42、拉丁 48)
function svgC(sys, small = false) {
const g = G[sys.key];
const size = small ? (sys.cjk ? 42 : 48) : (sys.cjk ? 36 : 42);
const s = size / 1000;
const [x0, y0, x1, y1] = g.bbox;
const tx = 32 - s * (x0 + x1) / 2, ty = 32 - s * (y0 + y1) / 2; // 墨迹包围盒居中
const ring = small ? "" : ``);
return page.screenshot({ omitBackground: true, clip: { x: 0, y: 0, width: n, height: n } });
}
for (const sys of SYS) {
const dir = path.join(out, sys.key); fs.mkdirSync(dir, { recursive: true });
const svg = svgC(sys), svgSmall = svgC(sys, true);
fs.writeFileSync(path.join(dir, "favicon.svg"), svg + "\n");
const p16 = await raster(svgSmall, 16), p32 = await raster(svg, 32), p48 = await raster(svg, 48);
fs.writeFileSync(path.join(dir, "favicon-16.png"), p16);
fs.writeFileSync(path.join(dir, "favicon-32.png"), p32);
fs.writeFileSync(path.join(dir, "favicon-48.png"), p48);
fs.writeFileSync(path.join(dir, "favicon.ico"), ico([p16, p32, p48]));
fs.writeFileSync(path.join(dir, "apple-touch-icon.png"), await raster(svg, 180));
fs.writeFileSync(path.join(dir, "icon-192.png"), await raster(svg, 192));
fs.writeFileSync(path.join(dir, "icon-512.png"), await raster(svg, 512));
console.log("✓", sys.key, fs.readdirSync(dir).join(" "));
}
// 核对图:左 = 选型页里的 C(文字渲染),右 = 正式文件(路径),64px 放大 3 倍并排;再把 16px 放大 8 倍
const chooser = (k) => fs.readFileSync(path.resolve(here, "../svg", `C-${k}.svg`), "utf8");
const img = (svg, n, z) => `
`;
const pimg = (buf, n, z) => `
`;
let h = `
| 选型页 C(文字) | 正式 favicon.svg(路径) | 16px 专用版 | ico 里的 16 | ico 里的 32 | |
|---|---|---|---|---|---|
| ${sys.short} | ${img(chooser(sys.key), 64, 3)} | ${img(svgC(sys), 64, 3)} | ${img(svgC(sys, true), 64, 3)} | ${pimg(fs.readFileSync(path.join(dir, "favicon-16.png")), 16, 8)} | ${pimg(fs.readFileSync(path.join(dir, "favicon-32.png")), 32, 4)} |