feat: 接入可选字体包同源渲染链
This commit is contained in:
@@ -52,3 +52,33 @@ export function parseThemeResourceUrl(requestUrl: string) {
|
||||
assetPath: assetSegments.join("/")
|
||||
};
|
||||
}
|
||||
|
||||
export function parseFontPackResourceUrl(requestUrl: string) {
|
||||
const url = new URL(requestUrl);
|
||||
if (url.protocol !== "mdpdf:" || url.host !== "font-pack") {
|
||||
return undefined;
|
||||
}
|
||||
let segments: string[];
|
||||
try {
|
||||
segments = url.pathname
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map((segment) => decodeURIComponent(segment));
|
||||
} catch {
|
||||
throw new Error("字体包资源地址无效");
|
||||
}
|
||||
const [packId, packVersion, faceId, kind, ...extra] = segments;
|
||||
if (
|
||||
!packId ||
|
||||
!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(packId) ||
|
||||
!packVersion ||
|
||||
!/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(packVersion) ||
|
||||
!faceId ||
|
||||
!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(faceId) ||
|
||||
kind !== "web" ||
|
||||
extra.length > 0
|
||||
) {
|
||||
throw new Error("字体包资源地址无效");
|
||||
}
|
||||
return { packId, packVersion, faceId, kind } as const;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
createApplicationService,
|
||||
type ApplicationService
|
||||
} from "@md-to-pdf/application";
|
||||
import { parseThemeResourceUrl } from "./application-contract.js";
|
||||
import {
|
||||
parseFontPackResourceUrl,
|
||||
parseThemeResourceUrl
|
||||
} from "./application-contract.js";
|
||||
import {
|
||||
DesktopApplicationController
|
||||
} from "./desktop-application-controller.js";
|
||||
@@ -26,6 +29,7 @@ import {
|
||||
const APP_SCHEME = "mdpdf";
|
||||
const APP_HOST = "bundle";
|
||||
const THEME_HOST = "theme";
|
||||
const FONT_PACK_HOST = "font-pack";
|
||||
const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
|
||||
let applicationController: DesktopApplicationController | undefined;
|
||||
let pendingExternalMarkdownPath = findMarkdownFileArgument(
|
||||
@@ -102,6 +106,21 @@ function getDesktopThemeDirectory() {
|
||||
: path.resolve(currentDirectory, "../../..", ".local", "themes");
|
||||
}
|
||||
|
||||
function getDesktopFontPackDirectory() {
|
||||
if (!app.isPackaged) {
|
||||
return path.resolve(
|
||||
currentDirectory,
|
||||
"../../..",
|
||||
".local",
|
||||
"font-packs"
|
||||
);
|
||||
}
|
||||
const localApplicationData = process.env.LOCALAPPDATA?.trim();
|
||||
return localApplicationData
|
||||
? path.join(localApplicationData, "md-to-pdf", "font-packs")
|
||||
: path.join(app.getPath("userData"), "font-packs");
|
||||
}
|
||||
|
||||
function createDesktopApplicationService() {
|
||||
const projectRoot = path.resolve(currentDirectory, "../../..");
|
||||
return createApplicationService({
|
||||
@@ -111,6 +130,22 @@ function createDesktopApplicationService() {
|
||||
localRoot: getDesktopThemeDirectory(),
|
||||
createAssetUrl: (themeId, assetPath) =>
|
||||
`${APP_SCHEME}://${THEME_HOST}/${encodeURIComponent(themeId)}/${encodeThemeAssetPath(assetPath)}`,
|
||||
fontPacks: {
|
||||
roots: [getDesktopFontPackDirectory()],
|
||||
appVersion: app.getVersion(),
|
||||
createAssetUrl: (
|
||||
packId,
|
||||
version,
|
||||
faceId,
|
||||
kind,
|
||||
sha256
|
||||
) =>
|
||||
`${APP_SCHEME}://${FONT_PACK_HOST}/${encodeURIComponent(
|
||||
packId
|
||||
)}/${encodeURIComponent(version)}/${encodeURIComponent(
|
||||
faceId
|
||||
)}/${kind}?v=${sha256}`
|
||||
},
|
||||
onWarning: (message) => console.warn(message)
|
||||
});
|
||||
}
|
||||
@@ -155,6 +190,34 @@ async function registerApplicationProtocol(
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
}
|
||||
if (url.host === FONT_PACK_HOST) {
|
||||
try {
|
||||
const resource = parseFontPackResourceUrl(request.url);
|
||||
if (!resource) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
const asset = await applicationService.getFontPackAsset(
|
||||
resource.packId,
|
||||
resource.packVersion,
|
||||
resource.faceId,
|
||||
resource.kind
|
||||
);
|
||||
if (!asset) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
return new Response(asset.content, {
|
||||
headers: {
|
||||
"access-control-allow-origin": "*",
|
||||
"cache-control": "public, max-age=31536000, immutable",
|
||||
"content-type": asset.contentType,
|
||||
etag: `\"${asset.sha256}\"`,
|
||||
"x-content-type-options": "nosniff"
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
}
|
||||
if (url.host !== APP_HOST) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
@@ -117,7 +117,10 @@ export function isAllowedPdfRuntimeUrl(
|
||||
if (["about:", "blob:", "data:"].includes(requested.protocol)) {
|
||||
return true;
|
||||
}
|
||||
if (requested.protocol === "mdpdf:" && requested.host === "theme") {
|
||||
if (
|
||||
requested.protocol === "mdpdf:" &&
|
||||
["theme", "font-pack"].includes(requested.host)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return requested.origin === new URL(renderUrl).origin;
|
||||
|
||||
Reference in New Issue
Block a user