🎨 Desktop export PDF supports no pagination https://ld246.com/article/1764933918963

This commit is contained in:
Jeffrey Chen 2025-12-07 20:06:00 +08:00
parent be53f83c59
commit 94d6f7c439
17 changed files with 138 additions and 32 deletions

View file

@ -738,6 +738,7 @@
"exportPDF3": "مقياس الصفحة",
"exportPDF4": "تضمين الأصول",
"exportPDF5": "البقاء مطوية",
"paged": "ترقيم الصفحات",
"mergeSubdocs": "إبقاء مطوياً",
"removeAssetsFolder": "إزالة دليل الأصول",
"upload": "رفع",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Seitenmaßstab",
"exportPDF4": "Assets einbetten",
"exportPDF5": "Aufgeklappt beibehalten",
"paged": "Seitenumbruch",
"mergeSubdocs": "Unterdokumente zusammenführen",
"removeAssetsFolder": "Assets-Verzeichnis entfernen",
"upload": "Hochladen",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Page Scale",
"exportPDF4": "Embed assets",
"exportPDF5": "Keep folded",
"paged": "Paged",
"mergeSubdocs": "Merge subdocuments",
"removeAssetsFolder": "Remove assets directory",
"upload": "Upload",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Escala de la página",
"exportPDF4": "Activos incrustados",
"exportPDF5": "Mantener doblado",
"paged": "Paginado",
"mergeSubdocs": "Fusionar subdocumentos",
"removeAssetsFolder": "Eliminar directorio de activos",
"upload": "Subir",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Échelle de page",
"exportPDF4": "Incorporer des ressources",
"exportPDF5": "Garder plié",
"paged": "Pagination",
"mergeSubdocs": "Fusionner les sous-documents",
"removeAssetsFolder": "Supprimer le répertoire des actifs",
"upload": "Télécharger",

View file

@ -738,6 +738,7 @@
"exportPDF3": "קנה מידה של עמוד",
"exportPDF4": "הכנס נכסים",
"exportPDF5": "שמור מקופל",
"paged": "עמודים",
"mergeSubdocs": "מזג תתי-מסמכים",
"removeAssetsFolder": "הסר תיקיית נכסים",
"upload": "העלה",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Scala pagina",
"exportPDF4": "Incorpora asset",
"exportPDF5": "Mantieni piegato",
"paged": "Paginazione",
"mergeSubdocs": "Unisci sotto-documenti",
"removeAssetsFolder": "Rimuovi directory asset",
"upload": "Carica",

View file

@ -738,6 +738,7 @@
"exportPDF3": "ページスケール",
"exportPDF4": "アセットの埋め込み",
"exportPDF5": "折りたたみを保持",
"paged": "ページ分割",
"mergeSubdocs": "サブドキュメントをマージ",
"removeAssetsFolder": "アセットディレクトリを削除",
"upload": "アップロード",

View file

@ -738,6 +738,7 @@
"exportPDF3": "페이지 비율",
"exportPDF4": "에셋 임베드",
"exportPDF5": "접힌 상태 유지",
"paged": "페이지 분할",
"mergeSubdocs": "하위 문서 병합",
"removeAssetsFolder": "assets 디렉토리 제거",
"upload": "업로드",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Skala strony",
"exportPDF4": "Osadź zasoby",
"exportPDF5": "Zachowaj złożone",
"paged": "Stronicowanie",
"mergeSubdocs": "Scal poddokumenty",
"removeAssetsFolder": "Usuń katalog zasobów",
"upload": "Prześlij",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Escala da página",
"exportPDF4": "Incorporar ativos",
"exportPDF5": "Manter dobrado",
"paged": "Paginação",
"mergeSubdocs": "Mesclar subdocumentos",
"removeAssetsFolder": "Remover diretório de ativos",
"upload": "Enviar",

View file

@ -738,6 +738,7 @@
"exportPDF3": "Масштаб страницы",
"exportPDF4": "Встраивать ресурсы",
"exportPDF5": "Сохранить сложенные",
"paged": "Разбивка на страницы",
"mergeSubdocs": "Объединить поддокументы",
"removeAssetsFolder": "Удалить директорию ресурсов",
"upload": "Загрузить",

View file

@ -738,6 +738,7 @@
"exportPDF3": "頁面縮放",
"exportPDF4": "嵌入資源文件",
"exportPDF5": "保持折疊狀態",
"paged": "分頁",
"mergeSubdocs": "合併子文檔",
"removeAssetsFolder": "移除 assets 目錄",
"upload": "上傳",

View file

@ -738,6 +738,7 @@
"exportPDF3": "页面缩放",
"exportPDF4": "嵌入资源文件",
"exportPDF5": "保持折叠状态",
"paged": "分页",
"mergeSubdocs": "合并子文档",
"removeAssetsFolder": "移除 assets 目录",
"upload": "上传",

View file

@ -208,6 +208,9 @@ export const initWindow = async (app: App) => {
});
ipcRenderer.on(Constants.SIYUAN_EXPORT_PDF, async (e, ipcData) => {
const msgId = showMessage(window.siyuan.languages.exporting, -1);
const isPaged = ipcData.paged !== undefined ? ipcData.paged : true;
// 分页模式下 pageSize 是字符串,不分页模式下是对象,统一使用 pageSizeValue 获取原始页面大小
const pageSizeValue = ipcData.pageSizeValue !== undefined ? ipcData.pageSizeValue : (isPaged ? ipcData.pdfOptions.pageSize : "A4");
window.siyuan.storage[Constants.LOCAL_EXPORTPDF] = {
removeAssets: ipcData.removeAssets,
keepFold: ipcData.keepFold,
@ -215,16 +218,17 @@ export const initWindow = async (app: App) => {
watermark: ipcData.watermark,
landscape: ipcData.pdfOptions.landscape,
marginType: ipcData.pdfOptions.marginType,
pageSize: ipcData.pdfOptions.pageSize,
pageSize: pageSizeValue,
scale: ipcData.pdfOptions.scale,
marginTop: ipcData.pdfOptions.margins.top,
marginRight: ipcData.pdfOptions.margins.right,
marginBottom: ipcData.pdfOptions.margins.bottom,
marginLeft: ipcData.pdfOptions.margins.left,
paged: isPaged,
};
setStorageVal(Constants.LOCAL_EXPORTPDF, window.siyuan.storage[Constants.LOCAL_EXPORTPDF]);
try {
if (window.siyuan.config.export.pdfFooter.trim()) {
if (window.siyuan.config.export.pdfFooter.trim() && isPaged) {
const response = await fetchSyncPost("/api/template/renderSprig", {template: window.siyuan.config.export.pdfFooter});
ipcData.pdfOptions.displayHeaderFooter = true;
ipcData.pdfOptions.headerTemplate = "<span></span>";

View file

@ -334,6 +334,13 @@ const renderPDF = async (id: string) => {
<span class="fn__hr"></span>
<input id="watermark" class="b3-switch" type="checkbox" ${localData.watermark ? "checked" : ""}>
</label>
<label class="b3-label">
<div>
${window.siyuan.languages.paged}
</div>
<span class="fn__hr"></span>
<input id="paged" class="b3-switch" type="checkbox" ${(localData.paged !== undefined ? localData.paged : (localData.continuous !== undefined ? !localData.continuous : true)) ? "checked" : ""}>
</label>
</div>
<div class="fn__flex" style="padding: 0 16px">
<div class="fn__flex-1"></div>
@ -594,35 +601,115 @@ ${getIconScript(servePath)}
});
actionElement.querySelector('.b3-button--text').addEventListener('click', () => {
const {ipcRenderer} = require("electron");
ipcRenderer.send("${Constants.SIYUAN_EXPORT_PDF}", {
title: "${window.siyuan.languages.export} PDF",
pdfOptions:{
printBackground: true,
landscape: actionElement.querySelector("#landscape").checked,
marginType: actionElement.querySelector("#marginsType").value,
margins: {
top: parseFloat(document.querySelector("#marginsTop").value),
bottom: parseFloat(document.querySelector("#marginsBottom").value),
left: parseFloat(document.querySelector("#marginsLeft").value),
right: parseFloat(document.querySelector("#marginsRight").value),
},
scale: parseFloat(actionElement.querySelector("#scale").value),
pageSize: actionElement.querySelector("#pageSize").value,
},
keepFold: keepFoldElement.checked,
mergeSubdocs: mergeSubdocsElement.checked,
watermark: watermarkElement.checked,
removeAssets: actionElement.querySelector("#removeAssets").checked,
rootId: "${id}",
rootTitle: response.data.name,
parentWindowId: ${currentWindowId},
})
previewElement.classList.add("exporting");
previewElement.style.zoom = "";
previewElement.style.paddingTop = "6px";
previewElement.style.paddingBottom = "0";
fixBlockWidth();
actionElement.remove();
const pageSizeValue = actionElement.querySelector("#pageSize").value;
const isLandscape = actionElement.querySelector("#landscape").checked;
const isPaged = actionElement.querySelector("#paged").checked;
let pageSize = pageSizeValue;
if (!isPaged) {
previewElement.classList.add("exporting");
previewElement.style.zoom = "";
previewElement.style.paddingTop = "6px";
previewElement.style.paddingBottom = "0";
fixBlockWidth();
const calculateAndExport = () => {
const getPageSizeDimensions = (pageSizeName, isLandscape) => {
const pageSizes = {
"A3": { width: 11.69, height: 16.54 },
"A4": { width: 8.27, height: 11.69 },
"A5": { width: 5.83, height: 8.27 },
"Legal": { width: 8.5, height: 14 },
"Letter": { width: 8.5, height: 11 },
"Tabloid": { width: 11, height: 17 },
};
const size = pageSizes[pageSizeName] || pageSizes["A4"];
return isLandscape ? { width: size.height, height: size.width } : size;
};
const previewContent = previewElement.querySelector(".protyle-wysiwyg") || previewElement;
const contentHeight = previewContent.scrollHeight || previewContent.offsetHeight;
// 将像素高度转换为英寸1 英寸 = 96 像素),并添加一些边距
const heightInches = Math.max(contentHeight / 96 + 2, 11); // 最小高度 11 英寸
// 获取选择的页面大小的宽度
const selectedPageSize = getPageSizeDimensions(pageSizeValue, isLandscape);
const marginTop = parseFloat(document.querySelector("#marginsTop").value) || 0;
const marginBottom = parseFloat(document.querySelector("#marginsBottom").value) || 0;
const totalHeightInches = heightInches + marginTop + marginBottom;
// 使用选择的页面宽度,高度根据内容动态计算
const widthInches = isLandscape ? totalHeightInches : selectedPageSize.width;
const finalHeightInches = isLandscape ? selectedPageSize.width : totalHeightInches;
pageSize = {
width: widthInches,
height: finalHeightInches
};
ipcRenderer.send("${Constants.SIYUAN_EXPORT_PDF}", {
title: "${window.siyuan.languages.export} PDF",
pdfOptions:{
printBackground: true,
landscape: isLandscape,
marginType: actionElement.querySelector("#marginsType").value,
margins: {
top: marginTop,
bottom: marginBottom,
left: parseFloat(document.querySelector("#marginsLeft").value) || 0,
right: parseFloat(document.querySelector("#marginsRight").value) || 0,
},
scale: parseFloat(actionElement.querySelector("#scale").value),
pageSize: pageSize,
},
keepFold: keepFoldElement.checked,
mergeSubdocs: mergeSubdocsElement.checked,
watermark: watermarkElement.checked,
removeAssets: actionElement.querySelector("#removeAssets").checked,
paged: isPaged,
pageSizeValue: pageSizeValue,
rootId: "${id}",
rootTitle: response.data.name,
parentWindowId: ${currentWindowId},
});
actionElement.remove();
};
// 使用 requestAnimationFrame 确保布局完成
requestAnimationFrame(() => {
requestAnimationFrame(calculateAndExport);
});
} else {
ipcRenderer.send("${Constants.SIYUAN_EXPORT_PDF}", {
title: "${window.siyuan.languages.export} PDF",
pdfOptions:{
printBackground: true,
landscape: isLandscape,
marginType: actionElement.querySelector("#marginsType").value,
margins: {
top: parseFloat(document.querySelector("#marginsTop").value),
bottom: parseFloat(document.querySelector("#marginsBottom").value),
left: parseFloat(document.querySelector("#marginsLeft").value),
right: parseFloat(document.querySelector("#marginsRight").value),
},
scale: parseFloat(actionElement.querySelector("#scale").value),
pageSize: pageSize,
},
keepFold: keepFoldElement.checked,
mergeSubdocs: mergeSubdocsElement.checked,
watermark: watermarkElement.checked,
removeAssets: actionElement.querySelector("#removeAssets").checked,
paged: isPaged,
pageSizeValue: pageSizeValue,
rootId: "${id}",
rootTitle: response.data.name,
parentWindowId: ${currentWindowId},
})
previewElement.classList.add("exporting");
previewElement.style.zoom = "";
previewElement.style.paddingTop = "6px";
previewElement.style.paddingBottom = "0";
fixBlockWidth();
actionElement.remove();
}
});
setPadding();
renderPreview(response.data);

View file

@ -436,7 +436,8 @@ export const getLocalStorage = (cb: () => void) => {
removeAssets: true,
keepFold: false,
mergeSubdocs: false,
watermark: false
watermark: false,
paged: true
};
defaultStorage[Constants.LOCAL_EXPORTIMG] = {
keepFold: false,