mirror of
https://github.com/2234839/web-font.git
synced 2026-09-04 14:53:32 +08:00
fix: 更新字体输出格式为 ttf,优化加载性能
This commit is contained in:
parent
8e2c68e6f9
commit
cc792d27b0
@ -13,7 +13,7 @@ This project subsets fonts on the server side — pass in the text you need, get
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=woff2") format("woff2");
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=ttf") format("truetype");
|
||||
}
|
||||
.title { font-family: "MyFont", serif; }
|
||||
</style>
|
||||
@ -97,7 +97,7 @@ No SDK needed. One CSS block to use any Chinese font. Server returns only the ch
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=你的文字&outType=woff2") format("woff2");
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=你的文字&outType=ttf") format("truetype");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=woff2") format("woff2");
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=ttf") format("truetype");
|
||||
}
|
||||
.title { font-family: "MyFont", serif; }
|
||||
</style>
|
||||
@ -98,7 +98,7 @@ Prompt: "生成一个东方禅意风格的茶品牌官网"
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=你的文字&outType=woff2") format("woff2");
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=你的文字&outType=ttf") format("truetype");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ export async function handleGetConfig(req: Request, _res: Response) {
|
||||
res: jsonResponse({
|
||||
enableTempUpload,
|
||||
adminUploadEnabled: !!adminApiKey,
|
||||
supportedOutTypes: ["woff2", "ttf"],
|
||||
supportedOutTypes: ["ttf", "woff2"],
|
||||
/** 临时字体保留时限(秒) */
|
||||
tempRetentionSeconds,
|
||||
/** 字体子集化最大并发数 */
|
||||
|
||||
@ -48,7 +48,7 @@ const blob = await leafer.export('png', { pixelRatio: 2 })
|
||||
```ts
|
||||
new WebFontPlugin(leafer, {
|
||||
baseUrl: 'https://webfont.shenzilong.cn', // 自部署时改这里
|
||||
outType: 'woff2',
|
||||
outType: 'ttf', // 默认 ttf:子集场景无 brotli 编码/解码开销,端到端更快;流量敏感可改 'woff2'
|
||||
debounceMs: 120,
|
||||
watch: true, // 持续监听画布变化;静态海报导出可关掉
|
||||
debug: false,
|
||||
|
||||
@ -16,7 +16,7 @@ export interface IWebFontOptions {
|
||||
baseUrl?: string
|
||||
/** CSS font-family 名,默认去掉扩展名的字体名 */
|
||||
family?: string
|
||||
/** 输出格式,默认 woff2 */
|
||||
/** 输出格式,默认 ttf:子集场景无 brotli 编码/解码开销,端到端更快(几 KB 的子集体积差异可忽略) */
|
||||
outType?: 'woff2' | 'ttf'
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ export class WebFontCSSMode {
|
||||
ensurePreconnect(baseUrl)
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? 'woff2',
|
||||
outType: options.outType ?? 'ttf',
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family),
|
||||
})
|
||||
|
||||
@ -175,7 +175,7 @@ export class WebFontCSSMode {
|
||||
ensurePreconnect(baseUrl)
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? 'woff2',
|
||||
outType: options.outType ?? 'ttf',
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family),
|
||||
})
|
||||
|
||||
@ -248,7 +248,7 @@ export class WebFontCSSMode {
|
||||
ensurePreconnect(baseUrl)
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? 'woff2',
|
||||
outType: options.outType ?? 'ttf',
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family),
|
||||
})
|
||||
this.engine.submitText(key, options.text)
|
||||
|
||||
@ -15,7 +15,7 @@ export interface IFontFaceOptions {
|
||||
baseUrl?: string
|
||||
/** 注册用的 family 名,默认去掉扩展名 */
|
||||
family?: string
|
||||
/** 输出格式,默认 woff2(Node 端自动降级 ttf,见 loadFontFace) */
|
||||
/** 输出格式,默认 ttf:子集场景无 brotli 编码/解码开销,端到端更快(Node 端也强制 ttf,见 loadFontFace) */
|
||||
outType?: 'woff2' | 'ttf'
|
||||
}
|
||||
|
||||
@ -80,8 +80,8 @@ export class WebFontFontFaceMode {
|
||||
const family = options.family ?? fontName.replace(/\.(ttf|otf|woff2?|ttc)$/i, '').trim()
|
||||
const key = IncrementalEngine.fontKey(fontName, family)
|
||||
const baseUrl = options.baseUrl ?? this.defaultBaseUrl
|
||||
/** Node 模式统一用 ttf:woff2 注册返回 null 不可靠(探针实证) */
|
||||
const outType = this.nodeEnv ? 'ttf' : options.outType ?? 'woff2'
|
||||
/** Node 模式统一用 ttf:woff2 注册返回 null 不可靠(探针实证);浏览器默认也是 ttf(见 IFontFaceOptions.outType 注释) */
|
||||
const outType = this.nodeEnv ? 'ttf' : options.outType ?? 'ttf'
|
||||
|
||||
/** 浏览器分支:FontFace + unicodeRange(多 chunk 同 family 按字符精确生效) */
|
||||
const handleChunkBrowser = async (chunk: LoadedChunk): Promise<void> => {
|
||||
|
||||
@ -250,7 +250,7 @@
|
||||
ensurePreconnect(baseUrl);
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? "woff2",
|
||||
outType: options.outType ?? "ttf",
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family)
|
||||
});
|
||||
if (this.pollTasks.has(options.selector)) clearInterval(this.pollTasks.get(options.selector).timer);
|
||||
@ -273,7 +273,7 @@
|
||||
ensurePreconnect(baseUrl);
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? "woff2",
|
||||
outType: options.outType ?? "ttf",
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family)
|
||||
});
|
||||
if (this.observeTasks.has(options.selector)) this.observeTasks.get(options.selector).dispose();
|
||||
@ -323,7 +323,7 @@
|
||||
ensurePreconnect(baseUrl);
|
||||
this.engine.ensureState(key, options.fontName, {
|
||||
baseUrl,
|
||||
outType: options.outType ?? "woff2",
|
||||
outType: options.outType ?? "ttf",
|
||||
onLoadChunk: this.makeOnLoadChunk(key, family)
|
||||
});
|
||||
this.engine.submitText(key, options.text);
|
||||
@ -519,8 +519,8 @@
|
||||
const family = options.family ?? fontName.replace(/\.(ttf|otf|woff2?|ttc)$/i, "").trim();
|
||||
const key = IncrementalEngine.fontKey(fontName, family);
|
||||
const baseUrl = options.baseUrl ?? this.defaultBaseUrl;
|
||||
/** Node 模式统一用 ttf:woff2 注册返回 null 不可靠(探针实证) */
|
||||
const outType = this.nodeEnv ? "ttf" : options.outType ?? "woff2";
|
||||
/** Node 模式统一用 ttf:woff2 注册返回 null 不可靠(探针实证);浏览器默认也是 ttf(见 IFontFaceOptions.outType 注释) */
|
||||
const outType = this.nodeEnv ? "ttf" : options.outType ?? "ttf";
|
||||
/** 浏览器分支:FontFace + unicodeRange(多 chunk 同 family 按字符精确生效) */
|
||||
const handleChunkBrowser = async (chunk) => {
|
||||
const unicodeRanges = chunk.chars.map((c) => "U+" + c.codePointAt(0).toString(16).padStart(4, "0")).join(", ");
|
||||
|
||||
@ -15,7 +15,7 @@ description: 中文字体按需裁剪与加载能力,可让 AI 在生成中文
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=woff2") format("woff2");
|
||||
src: url("https://webfont.shenzilong.cn/api?font=令东齐伋复刻体&text=静心茶舍&outType=ttf") format("truetype");
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@ -33,13 +33,13 @@ GET https://webfont.shenzilong.cn/api/fonts
|
||||
### 其他接口
|
||||
|
||||
```
|
||||
GET https://webfont.shenzilong.cn/api?font={name}&text={chars}&outType=woff2 — 裁剪字体
|
||||
GET https://webfont.shenzilong.cn/api?font={name}&text={chars}&outType=ttf — 裁剪字体
|
||||
GET https://webfont.shenzilong.cn/api/fonts — 列出可用字体
|
||||
GET https://webfont.shenzilong.cn/api/config — 服务配置
|
||||
POST https://webfont.shenzilong.cn/api/upload?mode=temp — 临时上传字体
|
||||
```
|
||||
|
||||
字体名支持模糊匹配(精确 > 前缀 > 包含)。`outType` 支持 `woff2` 和 `ttf`。
|
||||
字体名支持模糊匹配(精确 > 前缀 > 包含)。`outType` 支持 `ttf`(默认,服务端裁剪快 2~6 倍,子集场景端到端更快)和 `woff2`(体积小 ~50%,流量敏感时用)。
|
||||
|
||||
### SDK 增量加载
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ onMounted(() => {
|
||||
fontName: "令东齐伋复刻体.ttf",
|
||||
selector: ".demo-after .zh-font",
|
||||
family: "ZenSerif",
|
||||
outType: "woff2",
|
||||
outType: "ttf",
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -84,8 +84,8 @@ const messages = {
|
||||
outputFormat: "输出格式",
|
||||
woff2Label: "WOFF2 体积更小",
|
||||
ttfLabel: "TTF 速度更快",
|
||||
woff2Desc: "约压缩 50%,适合生产",
|
||||
ttfDesc: "无编码开销,适合开发",
|
||||
woff2Desc: "体积小约 50%,流量敏感时用",
|
||||
ttfDesc: "无编码/解码开销,端到端更快",
|
||||
|
||||
// UploadSection.vue
|
||||
uploadTip: "支持 .ttf 和 .otf 格式,建议上传 .ttf 字体文件以获得最佳兼容性",
|
||||
@ -194,8 +194,8 @@ const messages = {
|
||||
outputFormat: "Format",
|
||||
woff2Label: "WOFF2 Smaller",
|
||||
ttfLabel: "TTF Faster",
|
||||
woff2Desc: "~50% smaller, for production",
|
||||
ttfDesc: "No encoding overhead, for dev",
|
||||
woff2Desc: "~50% smaller, for traffic-sensitive cases",
|
||||
ttfDesc: "No encode/decode overhead, faster end-to-end",
|
||||
|
||||
// UploadSection.vue
|
||||
uploadTip: "Supports .ttf and .otf. .ttf recommended for best compatibility",
|
||||
|
||||
@ -116,16 +116,16 @@ const allPreviewText = computed(() => {
|
||||
/** 子集字体 URL(合并所有预览文字) */
|
||||
const subsetFontUrl = computed(() => {
|
||||
if (!allPreviewText.value) return "";
|
||||
return `${origin.value}/api?font=${encodeURIComponent(fontName.value)}&text=${encodeURIComponent(allPreviewText.value)}&outType=woff2`;
|
||||
return `${origin.value}/api?font=${encodeURIComponent(fontName.value)}&text=${encodeURIComponent(allPreviewText.value)}&outType=ttf`;
|
||||
});
|
||||
|
||||
/** 动态注入 @font-face —— API 返回二进制 woff2,需用 @font-face 引入而非 <link rel=stylesheet> */
|
||||
/** 动态注入 @font-face —— API 返回二进制 ttf,需用 @font-face 引入而非 <link rel=stylesheet> */
|
||||
let injectedStyle: HTMLStyleElement | null = null;
|
||||
watchEffect(() => {
|
||||
/** SSG/SSR 阶段没有 document */
|
||||
if (typeof document === "undefined") return;
|
||||
const css = subsetFontUrl.value
|
||||
? `@font-face { font-family: "${fontName.value}"; src: url("${subsetFontUrl.value}") format("woff2"); }`
|
||||
? `@font-face { font-family: "${fontName.value}"; src: url("${subsetFontUrl.value}") format("truetype"); }`
|
||||
: "";
|
||||
if (!css) return;
|
||||
if (!injectedStyle) {
|
||||
@ -155,7 +155,7 @@ watchEffect(() => {
|
||||
fontName: font,
|
||||
text,
|
||||
family: font,
|
||||
outType: "woff2",
|
||||
outType: "ttf",
|
||||
}) ?? null;
|
||||
} else {
|
||||
charsetLoader.update(text);
|
||||
@ -167,7 +167,7 @@ const usageCode = computed(() =>
|
||||
`<style>
|
||||
@font-face {
|
||||
font-family: "${fontName.value}";
|
||||
src: url("${origin.value}/api?font=${fontName.value}&text=你的文字&outType=woff2") format("woff2");
|
||||
src: url("${origin.value}/api?font=${fontName.value}&text=你的文字&outType=ttf") format("truetype");
|
||||
}
|
||||
.my-title { font-family: "${fontName.value}"; }
|
||||
</style>`
|
||||
|
||||
@ -44,7 +44,7 @@ const outType = ref<"woff2" | "ttf">("ttf");
|
||||
const serverConfig = ref<ServerConfig>({
|
||||
enableTempUpload: false,
|
||||
adminUploadEnabled: false,
|
||||
supportedOutTypes: ["woff2", "ttf"],
|
||||
supportedOutTypes: ["ttf", "woff2"],
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
@ -60,7 +60,7 @@ onMounted(async () => {
|
||||
|
||||
const [fontList, config] = await Promise.all([
|
||||
fetchFonts().catch(() => [] as FontInfo[]),
|
||||
fetchConfig().catch((): ServerConfig => ({ enableTempUpload: false, adminUploadEnabled: false, supportedOutTypes: ["woff2", "ttf"] })),
|
||||
fetchConfig().catch((): ServerConfig => ({ enableTempUpload: false, adminUploadEnabled: false, supportedOutTypes: ["ttf", "woff2"] })),
|
||||
]);
|
||||
fonts.value = fontList;
|
||||
serverConfig.value = config;
|
||||
@ -220,7 +220,7 @@ async function refreshFonts() {
|
||||
:fonts="fonts"
|
||||
:selectedFont="selectedFont"
|
||||
:onFontChange="onFontChange"
|
||||
:supportedOutTypes="serverConfig.supportedOutTypes || ['woff2', 'ttf']"
|
||||
:supportedOutTypes="serverConfig.supportedOutTypes || ['ttf', 'woff2']"
|
||||
:outType="outType"
|
||||
:onOutTypeChange="(v: 'woff2' | 'ttf') => outType = v"
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user