feat: 接入可选字体包同源渲染链
This commit is contained in:
@@ -3,6 +3,7 @@ import type { FastifyInstance } from "fastify";
|
||||
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import { buildApp } from "../src/app.js";
|
||||
import {
|
||||
createPdfContentDisposition,
|
||||
@@ -39,6 +40,61 @@ function localThemeManifest(
|
||||
};
|
||||
}
|
||||
|
||||
function sha256(content: Uint8Array | string) {
|
||||
return createHash("sha256").update(content).digest("hex");
|
||||
}
|
||||
|
||||
async function createServerFontPack(root: string) {
|
||||
const directory = join(root, "official-cjk", "1.0.0");
|
||||
const web = Buffer.from([31, 32, 33, 34]);
|
||||
const docx = Buffer.from([41, 42, 43, 44, 45]);
|
||||
const license = "SIL Open Font License 1.1";
|
||||
await mkdir(join(directory, "fonts"), { recursive: true });
|
||||
await writeFile(join(directory, "fonts", "serif.woff2"), web);
|
||||
await writeFile(join(directory, "fonts", "serif.ttf"), docx);
|
||||
await writeFile(join(directory, "OFL.txt"), license, "utf8");
|
||||
await writeFile(
|
||||
join(directory, "font-pack.json"),
|
||||
JSON.stringify({
|
||||
manifestVersion: 1,
|
||||
id: "official-cjk",
|
||||
version: "1.0.0",
|
||||
name: "政务中文字体包",
|
||||
description: "Server 字体包资源测试",
|
||||
license: "OFL-1.1",
|
||||
licenseFile: "OFL.txt",
|
||||
licenseSha256: sha256(license),
|
||||
licenseBytes: Buffer.byteLength(license),
|
||||
compatibility: {
|
||||
minimumAppVersion: "0.6.0",
|
||||
maximumAppVersionExclusive: "0.7.0"
|
||||
},
|
||||
faces: [
|
||||
{
|
||||
id: "serif-regular",
|
||||
targets: ["FandolSong", "Mdpdf Fandol Song"],
|
||||
weight: 400,
|
||||
style: "normal",
|
||||
web: {
|
||||
path: "fonts/serif.woff2",
|
||||
format: "woff2",
|
||||
bytes: web.byteLength,
|
||||
sha256: sha256(web)
|
||||
},
|
||||
docx: {
|
||||
path: "fonts/serif.ttf",
|
||||
format: "truetype",
|
||||
bytes: docx.byteLength,
|
||||
sha256: sha256(docx)
|
||||
}
|
||||
}
|
||||
]
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
return { web };
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await app?.close();
|
||||
if (temporaryDirectory) {
|
||||
@@ -136,6 +192,43 @@ describe("预览 API", () => {
|
||||
expect(response.body).toContain("#write");
|
||||
});
|
||||
|
||||
it("为主题 CSS 提供经过注册器验证的字体包 Web 资源", async () => {
|
||||
temporaryDirectory = await mkdtemp(join(tmpdir(), "md-to-pdf-font-pack-"));
|
||||
const fontPack = await createServerFontPack(temporaryDirectory);
|
||||
app = buildApp({
|
||||
logger: false,
|
||||
fontPacks: {
|
||||
roots: [temporaryDirectory],
|
||||
appVersion: "0.6.0"
|
||||
}
|
||||
});
|
||||
|
||||
const cssResponse = await app.inject({
|
||||
method: "GET",
|
||||
url: "/api/themes/gov-red-standard/css"
|
||||
});
|
||||
expect(cssResponse.statusCode).toBe(200);
|
||||
expect(cssResponse.body).toContain("md-to-pdf-font-packs:official-cjk@1.0.0");
|
||||
expect(cssResponse.body).toContain(
|
||||
"/api/font-packs/official-cjk/1.0.0/serif-regular/web?v="
|
||||
);
|
||||
|
||||
const assetResponse = await app.inject({
|
||||
method: "GET",
|
||||
url: `/api/font-packs/official-cjk/1.0.0/serif-regular/web?v=${sha256(fontPack.web)}`
|
||||
});
|
||||
expect(assetResponse.statusCode).toBe(200);
|
||||
expect(assetResponse.headers["content-type"]).toContain("font/woff2");
|
||||
expect(assetResponse.headers["cache-control"]).toContain("immutable");
|
||||
expect(assetResponse.rawPayload).toEqual(fontPack.web);
|
||||
|
||||
const missingResponse = await app.inject({
|
||||
method: "GET",
|
||||
url: "/api/font-packs/official-cjk/2.0.0/serif-regular/web"
|
||||
});
|
||||
expect(missingResponse.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("发现本地主题并安全提供相对字体资源", async () => {
|
||||
temporaryDirectory = await mkdtemp(join(tmpdir(), "md-to-pdf-theme-"));
|
||||
const themeDirectory = join(temporaryDirectory, "local-test");
|
||||
|
||||
Reference in New Issue
Block a user