fix: 完善本地与网络图片渲染
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
# Desktop 桌面端
|
||||
|
||||
`apps/desktop` 是 `v0.4.0` 新增的 Electron 桌面应用。当前已经完成
|
||||
Electron 壳、受限 preload、进程内应用服务、隐藏分页窗口、Electron
|
||||
Chromium PDF 输出、原生 PDF 另存为和 Windows 安装包验证。
|
||||
Electron 壳、受限 preload、进程内应用服务、原生 Markdown 文件选择、
|
||||
同目录图片解析、隐藏分页窗口、Electron Chromium PDF 输出、原生 PDF
|
||||
另存为和 Windows 安装包验证。
|
||||
|
||||
## 开发
|
||||
|
||||
@@ -26,7 +27,7 @@ npm run make -w @md-to-pdf/desktop
|
||||
```
|
||||
|
||||
版本化目录包、Squirrel `Setup.exe` 和 ZIP 输出到
|
||||
`apps/desktop/out/v0.4.0/`。该目录被 Git 忽略。公司内部分发以
|
||||
`apps/desktop/out/v0.4.1/`。该目录被 Git 忽略。公司内部分发以
|
||||
`Setup.exe` 为正式安装包,ZIP 作为免安装辅助包;当前未配置代码签名,
|
||||
Windows 首次运行可能显示“未知发布者”提示。
|
||||
|
||||
@@ -42,7 +43,10 @@ Web 静态资源作为只读资源放在 Electron `resources/dist` 下,生产
|
||||
|
||||
- 应用窗口和 PDF 窗口均关闭 Node Integration;
|
||||
- 启用 Context Isolation 和 Chromium Sandbox;
|
||||
- preload 只暴露渲染、主题、生成 PDF 和保存 PDF 所需的窄接口;
|
||||
- preload 只暴露打开 Markdown、渲染、主题、生成 PDF 和保存 PDF 所需
|
||||
的窄接口;
|
||||
- 本地图片只能从当前 Markdown 所在目录内读取,并拒绝路径穿越和符号
|
||||
链接越界;
|
||||
- 主进程校验 IPC 发送者和分页载荷;
|
||||
- PDF 窗口使用独立内存 Session,并阻止外部网络资源;
|
||||
- 应用页面使用自定义协议,不使用 `file://`。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@md-to-pdf/desktop",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"private": true,
|
||||
"productName": "Markdown PDF 导出器",
|
||||
"description": "Markdown PDF 导出器桌面端",
|
||||
@@ -8,7 +8,7 @@
|
||||
"type": "module",
|
||||
"main": "dist/main.js",
|
||||
"dependencies": {
|
||||
"@md-to-pdf/application": "0.4.0"
|
||||
"@md-to-pdf/application": "0.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run clean && npm run build:main && npm run build:preload",
|
||||
|
||||
@@ -5,11 +5,13 @@ import {
|
||||
DESKTOP_GET_THEME_CSS,
|
||||
DESKTOP_GENERATE_PDF,
|
||||
DESKTOP_LIST_THEMES,
|
||||
DESKTOP_OPEN_MARKDOWN,
|
||||
DESKTOP_RENDER_MARKDOWN,
|
||||
DESKTOP_SAVE_PDF
|
||||
} from "./channels.js";
|
||||
|
||||
contextBridge.exposeInMainWorld("mdToPdfDesktop", {
|
||||
openMarkdown: () => ipcRenderer.invoke(DESKTOP_OPEN_MARKDOWN),
|
||||
renderMarkdown: (input: MarkdownRenderRequest) =>
|
||||
ipcRenderer.invoke(DESKTOP_RENDER_MARKDOWN, input),
|
||||
listThemes: () => ipcRenderer.invoke(DESKTOP_LIST_THEMES),
|
||||
|
||||
@@ -12,7 +12,8 @@ export function parseMarkdownRenderRequest(
|
||||
}
|
||||
return {
|
||||
markdown: value.markdown,
|
||||
language: value.language
|
||||
language: value.language,
|
||||
resources: value.resources
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export const DESKTOP_RENDER_MARKDOWN =
|
||||
"md-to-pdf:desktop:render-markdown";
|
||||
export const DESKTOP_OPEN_MARKDOWN =
|
||||
"md-to-pdf:desktop:open-markdown";
|
||||
export const DESKTOP_LIST_THEMES =
|
||||
"md-to-pdf:desktop:list-themes";
|
||||
export const DESKTOP_GET_THEME_CSS =
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
session,
|
||||
type IpcMainInvokeEvent
|
||||
} from "electron";
|
||||
import { access, writeFile } from "node:fs/promises";
|
||||
import { access, readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import {
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
DESKTOP_GET_THEME_CSS,
|
||||
DESKTOP_GENERATE_PDF,
|
||||
DESKTOP_LIST_THEMES,
|
||||
DESKTOP_OPEN_MARKDOWN,
|
||||
DESKTOP_RENDER_MARKDOWN,
|
||||
DESKTOP_SAVE_PDF
|
||||
} from "./channels.js";
|
||||
@@ -229,7 +230,31 @@ async function createApplication(
|
||||
event.senderFrame !== null &&
|
||||
new URL(event.senderFrame.url).origin ===
|
||||
new URL(applicationUrl).origin;
|
||||
let documentRoot: string | undefined;
|
||||
|
||||
ipcMain.handle(DESKTOP_OPEN_MARKDOWN, async (event) => {
|
||||
if (!validateSender(event)) {
|
||||
throw new Error("拒绝未授权的桌面文件请求");
|
||||
}
|
||||
const selection = await dialog.showOpenDialog(window, {
|
||||
title: "打开 Markdown",
|
||||
properties: ["openFile"],
|
||||
filters: [
|
||||
{ name: "Markdown 文件", extensions: ["md", "markdown"] },
|
||||
{ name: "文本文件", extensions: ["txt"] }
|
||||
]
|
||||
});
|
||||
const filePath = selection.filePaths[0];
|
||||
if (selection.canceled || !filePath) {
|
||||
return undefined;
|
||||
}
|
||||
const content = await readFile(filePath, "utf8");
|
||||
documentRoot = path.dirname(filePath);
|
||||
return {
|
||||
markdown: content,
|
||||
fileName: path.basename(filePath)
|
||||
};
|
||||
});
|
||||
ipcMain.handle(
|
||||
DESKTOP_RENDER_MARKDOWN,
|
||||
async (event, unsafeRequest: unknown) => {
|
||||
@@ -237,7 +262,8 @@ async function createApplication(
|
||||
throw new Error("拒绝未授权的桌面渲染请求");
|
||||
}
|
||||
return applicationService.render(
|
||||
parseMarkdownRenderRequest(unsafeRequest)
|
||||
parseMarkdownRenderRequest(unsafeRequest),
|
||||
documentRoot ? { localRoot: documentRoot } : {}
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -317,6 +343,7 @@ async function createApplication(
|
||||
|
||||
window.once("ready-to-show", () => window.show());
|
||||
window.on("closed", () => {
|
||||
ipcMain.removeHandler(DESKTOP_OPEN_MARKDOWN);
|
||||
ipcMain.removeHandler(DESKTOP_RENDER_MARKDOWN);
|
||||
ipcMain.removeHandler(DESKTOP_LIST_THEMES);
|
||||
ipcMain.removeHandler(DESKTOP_GET_THEME_CSS);
|
||||
|
||||
Reference in New Issue
Block a user