diff --git a/app/electron/main.js b/app/electron/main.js
index 46d61f7dc..7e300f7b5 100644
--- a/app/electron/main.js
+++ b/app/electron/main.js
@@ -84,8 +84,10 @@ try {
}
} catch (e) {
console.error(e)
- require('electron').dialog.showErrorBox('创建配置目录失败 Failed to create config directory',
- '思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。\n\nSiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.')
+ require('electron').
+ dialog.
+ showErrorBox('创建配置目录失败 Failed to create config directory',
+ '思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。\n\nSiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.')
app.exit()
}
@@ -186,7 +188,8 @@ const boot = () => {
})
require('@electron/remote/main').enable(mainWindow.webContents)
- mainWindow.webContents.userAgent = 'SiYuan/' + appVer + ' https://b3log.org/siyuan Electron'
+ mainWindow.webContents.userAgent = 'SiYuan/' + appVer +
+ ' https://b3log.org/siyuan Electron'
// 发起互联网服务请求时绕过安全策略 https://github.com/siyuan-note/siyuan/issues/5516
mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
@@ -348,6 +351,12 @@ const boot = () => {
tray.destroy()
}
})
+ ipcMain.on('siyuan-export-pdf', (event, data) => {
+ mainWindow.webContents.send('siyuan-export-pdf', data)
+ })
+ ipcMain.on('siyuan-export-close', (event, data) => {
+ mainWindow.webContents.send('siyuan-export-close', data)
+ })
ipcMain.on('siyuan-quit', () => {
try {
const bounds = mainWindow.getBounds()
@@ -520,11 +529,13 @@ const initKernel = (initData) => {
'
net stop winnat\nnetsh interface ipv4 add excludedportrange protocol=tcp startport=6806 numberofports=1\nnet start winnat
')
break
case 22:
- showErrorWindow('⚠️ 创建配置目录失败 Failed to create config directory',
+ showErrorWindow(
+ '⚠️ 创建配置目录失败 Failed to create config directory',
`思源需要在用户家目录下创建配置文件夹(~/.config/siyuan),请确保该路径具有写入权限。
SiYuan needs to create a configuration folder (~/.config/siyuan) in the user\'s home directory. Please make sure that the path has write permissions.
`)
break
case 23:
- showErrorWindow('⚠️ 无法读写块树文件 Failed to access blocktree file',
+ showErrorWindow(
+ '⚠️ 无法读写块树文件 Failed to access blocktree file',
`块树文件正在被其他程序锁定或者已经损坏,请删除 工作空间/temp/ 文件夹后重启
The block tree file is being locked by another program or is damaged, please delete the workspace/temp/ folder and restart.
`)
break
case 0:
diff --git a/app/src/config/search.ts b/app/src/config/search.ts
index b98a37c77..5a21fc608 100644
--- a/app/src/config/search.ts
+++ b/app/src/config/search.ts
@@ -64,7 +64,8 @@ export const initConfigSearch = (element: HTMLElement) => {
// 关于
getLang(["about", "about1", "about2", "about3", "about4", "about5", "about6", "about7", "about8",
"about11", "about12", "about13", "about14", "about15", "about16",
- "slogan", "currentVer", "checkUpdate", "updatePath", "snapshotPassword", "systemLog"]),
+ "slogan", "currentVer", "checkUpdate", "updatePath", "snapshotPassword", "systemLog",
+ "autoDownloadUpdatePkg"]),
];
const inputElement = element.querySelector(".b3-form__icon input") as HTMLInputElement;
if (window.siyuan.config.system.container !== "ios") {
diff --git a/app/src/constants.ts b/app/src/constants.ts
index 020c563f7..603b6bd4e 100644
--- a/app/src/constants.ts
+++ b/app/src/constants.ts
@@ -29,6 +29,8 @@ export abstract class Constants {
public static readonly SIYUAN_INIT: string = "siyuan-init";
public static readonly SIYUAN_OPENURL: string = "siyuan-openurl";
public static readonly SIYUAN_SAVE_CLOSE: string = "siyuan-save-close";
+ public static readonly SIYUAN_EXPORT_PDF: string = "siyuan-export-pdf";
+ public static readonly SIYUAN_EXPORT_CLOSE: string = "siyuan-export-close";
// size
public static readonly SIZE_TOOLBAR_HEIGHT: number = 30;
diff --git a/app/src/protyle/export/index.ts b/app/src/protyle/export/index.ts
index 626ccd2b0..f10c433f8 100644
--- a/app/src/protyle/export/index.ts
+++ b/app/src/protyle/export/index.ts
@@ -1,7 +1,7 @@
import {hideMessage, showMessage} from "../../dialog/message";
import {Constants} from "../../constants";
/// #if !BROWSER
-import {PrintToPDFOptions, OpenDialogReturnValue} from "electron";
+import {OpenDialogReturnValue, ipcRenderer} from "electron";
import {BrowserWindow, dialog} from "@electron/remote";
import * as fs from "fs";
import * as path from "path";
@@ -18,94 +18,13 @@ import {replaceLocalPath} from "../../editor/rename";
export const saveExport = (option: { type: string, id: string }) => {
/// #if !BROWSER
if (option.type === "pdf") {
- const localData = JSON.parse(localStorage.getItem(Constants.LOCAL_EXPORTPDF) || JSON.stringify({
- printBackground: true,
- landscape: false,
- marginsType: 0,
- scaleFactor: 100,
- pageSize: "A4",
- removeAssets: true,
- }));
- const pdfDialog = new Dialog({
- title: "PDF " + window.siyuan.languages.config,
- content: `
-
-
-
-
-
-
-
-
-
-
`,
- width: "520px",
- });
- const btnsElement = pdfDialog.element.querySelectorAll(".b3-button");
- btnsElement[0].addEventListener("click", () => {
- pdfDialog.destroy();
- });
- btnsElement[1].addEventListener("click", () => {
- const options: PrintToPDFOptions = {
- printBackground: true,
- landscape: (pdfDialog.element.querySelector("#landscape") as HTMLInputElement).checked,
- marginsType: parseInt((pdfDialog.element.querySelector("#marginsType") as HTMLInputElement).value),
- scaleFactor: parseInt((pdfDialog.element.querySelector("#scaleFactor") as HTMLInputElement).value),
- pageSize: (pdfDialog.element.querySelector("#pageSize") as HTMLSelectElement).value,
- };
- const removeAssets = (pdfDialog.element.querySelector("#removeAssets") as HTMLInputElement).checked;
- localStorage.setItem(Constants.LOCAL_EXPORTPDF, JSON.stringify(Object.assign(options, {removeAssets})));
- if (window.siyuan.config.appearance.mode === 1) {
- confirmDialog(window.siyuan.languages.pdfTip, window.siyuan.languages.pdfConfirm, () => {
- getExportPath(option, options, removeAssets);
- pdfDialog.destroy();
- });
- } else {
- getExportPath(option, options, removeAssets);
- pdfDialog.destroy();
- }
- });
+ if (window.siyuan.config.appearance.mode === 1) {
+ confirmDialog(window.siyuan.languages.pdfTip, window.siyuan.languages.pdfConfirm, () => {
+ renderPDF(option.id);
+ });
+ } else {
+ renderPDF(option.id);
+ }
} else if (option.type === "word") {
const localData = localStorage.getItem(Constants.LOCAL_EXPORTWORD);
const wordDialog = new Dialog({
@@ -132,7 +51,7 @@ export const saveExport = (option: { type: string, id: string }) => {
btnsElement[1].addEventListener("click", () => {
const removeAssets = (wordDialog.element.querySelector("#removeAssets") as HTMLInputElement).checked;
localStorage.setItem(Constants.LOCAL_EXPORTWORD, removeAssets.toString());
- getExportPath(option, undefined, removeAssets);
+ getExportPath(option, removeAssets);
wordDialog.destroy();
});
} else {
@@ -142,7 +61,289 @@ export const saveExport = (option: { type: string, id: string }) => {
};
/// #if !BROWSER
-const getExportPath = (option: { type: string, id: string }, pdfOption?: PrintToPDFOptions, removeAssets?: boolean) => {
+const renderPDF = (id: string) => {
+ const localData = JSON.parse(localStorage.getItem(Constants.LOCAL_EXPORTPDF) || JSON.stringify({
+ printBackground: true,
+ landscape: false,
+ marginsType: 0,
+ scaleFactor: 100,
+ pageSize: "A4",
+ removeAssets: true,
+ }));
+ const servePath = window.location.protocol + "//" + window.location.host;
+ const win = new BrowserWindow({
+ show: true,
+ width: 1024, // 860
+ webPreferences: {
+ contextIsolation: false,
+ nodeIntegration: true,
+ webviewTag: true,
+ webSecurity: false,
+ },
+ });
+ ipcRenderer.on(Constants.SIYUAN_EXPORT_CLOSE, () => {
+ win.destroy();
+ });
+ ipcRenderer.on(Constants.SIYUAN_EXPORT_PDF, (e, ipcData) => {
+ dialog.showOpenDialog({
+ title: window.siyuan.languages.export + " PDF",
+ properties: ["createDirectory", "openDirectory"],
+ }).then((result: OpenDialogReturnValue) => {
+ if (!result.canceled) {
+ const msgId = showMessage(window.siyuan.languages.exporting, -1);
+ const filePath = result.filePaths[0].endsWith(ipcData.rootTitle) ? result.filePaths[0] : path.join(result.filePaths[0], replaceLocalPath(ipcData.rootTitle));
+ localStorage.setItem(Constants.LOCAL_EXPORTPDF, JSON.stringify(Object.assign(ipcData.pdfOptions, {removeAssets: ipcData.removeAssets})));
+ try {
+ win.webContents.printToPDF(ipcData.pdfOptions).then((pdfData) => {
+ fetchPost("/api/export/exportHTML", {
+ id: ipcData.rootId,
+ pdf: true,
+ removeAssets: ipcData.removeAssets,
+ savePath: filePath
+ }, () => {
+ const pdfFilePath = path.join(filePath, path.basename(filePath) + ".pdf");
+ fs.writeFileSync(pdfFilePath, pdfData);
+ fetchPost("/api/export/addPDFOutline", {
+ id: ipcData.rootId,
+ path: pdfFilePath
+ }, () => {
+ afterExport(pdfFilePath, msgId);
+ if (ipcData.removeAssets) {
+ const removePromise = (dir: string) => {
+ return new Promise(function (resolve) {
+ //先读文件夹
+ fs.stat(dir, function (err, stat) {
+ if (stat) {
+ if (stat.isDirectory()) {
+ fs.readdir(dir, function (err, files) {
+ files = files.map(file => path.join(dir, file)); // a/b a/m
+ Promise.all(files.map(file => removePromise(file))).then(function () {
+ fs.rmdir(dir, resolve);
+ });
+ });
+ } else {
+ fs.unlink(dir, resolve);
+ }
+ }
+ });
+ });
+ };
+ removePromise(path.join(filePath, "assets"));
+ }
+ win.destroy();
+ });
+ });
+ }).catch((error: string) => {
+ showMessage("Export PDF error:" + error, 0, "error", msgId);
+ win.destroy();
+ });
+ } catch (e) {
+ showMessage("Export PDF error:" + e + ". Export HTML and use Chrome's printing function to convert to PDF", 0, "error", msgId);
+ }
+ }
+ })
+ })
+ fetchPost("/api/export/exportPreviewHTML", {
+ id,
+ }, response => {
+ let pdfWidth = "";
+ if (localData.pageSize === "A3") {
+ if (localData.landscape) {
+ pdfWidth = "16.5";
+ } else {
+ pdfWidth = "11.7";
+ }
+ } else if (localData.pageSize === "A4") {
+ if (localData.landscape) {
+ pdfWidth = "11.7";
+ } else {
+ pdfWidth = "8.8";
+ }
+ } else if (localData.pageSize === "A5") {
+ if (localData.landscape) {
+ pdfWidth = "8.3";
+ } else {
+ pdfWidth = "5.8";
+ }
+ } else if (localData.pageSize === "Legal") {
+ if (localData.landscape) {
+ pdfWidth = "14";
+ } else {
+ pdfWidth = "8.5";
+ }
+ } else if (localData.pageSize === "Letter") {
+ if (localData.landscape) {
+ pdfWidth = "11";
+ } else {
+ pdfWidth = "8.5";
+ }
+ } else if (localData.pageSize === "Tabloid") {
+ if (localData.landscape) {
+ pdfWidth = "17";
+ } else {
+ pdfWidth = "11";
+ }
+ }
+ let pdfMargin = "0.66";
+ if (localData.landscape) {
+ pdfMargin = "1.69";
+ }
+ if (localData.marginsType !== 0) {
+ if (localData.landscape) {
+ pdfMargin = "1.69";
+ } else {
+ pdfMargin = "0.3";
+ }
+ }
+ const html = `
+
+
+
+
+
+
+
+
+
+ ${response.data.name} - ${window.siyuan.languages.siyuanNote} v${Constants.SIYUAN_VERSION}
+
+
+
+
+
${response.data.content.replace(/
+
+
+
+`;
+ win.loadURL("data:text/html;charset=UTF-8," + encodeURIComponent(html));
+ })
+}
+
+const getExportPath = (option: { type: string, id: string }, removeAssets?: boolean) => {
fetchPost("/api/block/getBlockInfo", {
id: option.id
}, (response) => {
@@ -192,7 +393,7 @@ const getExportPath = (option: { type: string, id: string }, pdfOption?: PrintTo
}
afterExport(savePath, msgId);
} else {
- onExport(exportResponse, savePath, option.type, pdfOption, removeAssets, msgId);
+ onExport(exportResponse, savePath, option.type, removeAssets, msgId);
}
});
}
@@ -200,97 +401,14 @@ const getExportPath = (option: { type: string, id: string }, pdfOption?: PrintTo
});
};
-const onExport = (data: IWebSocketData, filePath: string, type: string, pdfOptions?: PrintToPDFOptions, removeAssets?: boolean, msgId?: string) => {
+const onExport = (data: IWebSocketData, filePath: string, type: string, removeAssets?: boolean, msgId?: string) => {
let themeName = window.siyuan.config.appearance.themeLight;
let mode = 0;
if (["html", "htmlmd"].includes(type) && window.siyuan.config.appearance.mode === 1) {
themeName = window.siyuan.config.appearance.themeDark;
mode = 1;
}
- let style = "";
- const servePath = window.location.protocol + "//" + window.location.host;
- let cdn = servePath + "/stage/protyle";
- let mediaRenderScript = "";
- let urlPrefix = servePath + "/";
- let tableZoom = "";
- if (type === "pdf") {
- data.data.content = data.data.content.replace(/