diff --git a/src/main/constant.ts b/src/main/constant.ts
index d1a9e9b67c..a2107caa5c 100644
--- a/src/main/constant.ts
+++ b/src/main/constant.ts
@@ -29,8 +29,8 @@ export const PRINT_HTML_TEMPLATE = `
{{richtextCss}}
-
-
+
+
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index 9fbe04c4a2..ecc34508aa 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -1389,6 +1389,9 @@
"no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
"no_content": "There is nothing to export to Notion."
},
+ "pdf": {
+ "export": "Failed to export to PDF"
+ },
"siyuan": {
"export": "Failed to export to Siyuan Note, please check connection status and configuration according to documentation",
"no_config": "Siyuan Note API address or token is not configured"
@@ -1491,6 +1494,9 @@
"notion": {
"export": "Successfully exported to Notion"
},
+ "pdf": {
+ "export": "Successfully exported to PDF"
+ },
"siyuan": {
"export": "Successfully exported to Siyuan Note"
},
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 8ec7bcf361..8f0ca9bd0e 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -1390,6 +1390,9 @@
"no_api_key": "未配置 Notion API Key 或 Notion Database ID",
"no_content": "无可导出到 Notion 的内容"
},
+ "pdf": {
+ "export": "导出 PDF 失败"
+ },
"siyuan": {
"export": "导出思源笔记失败,请检查连接状态并对照文档检查配置",
"no_config": "未配置思源笔记 API 地址或令牌"
@@ -1492,6 +1495,9 @@
"notion": {
"export": "成功导出到 Notion"
},
+ "pdf": {
+ "export": "成功导出为 PDF"
+ },
"siyuan": {
"export": "导出到思源笔记成功"
},
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 2a7db994b8..7f2d7e6be8 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -1389,6 +1389,9 @@
"no_api_key": "未設定 Notion API Key 或 Notion Database ID",
"no_content": "沒有可匯出至 Notion 的內容"
},
+ "pdf": {
+ "export": "導出 PDF 失敗"
+ },
"siyuan": {
"export": "導出思源筆記失敗,請檢查連接狀態並對照文檔檢查配置",
"no_config": "未配置思源筆記 API 地址或令牌"
@@ -1491,6 +1494,9 @@
"notion": {
"export": "成功匯出到 Notion"
},
+ "pdf": {
+ "export": "成功導出為 PDF"
+ },
"siyuan": {
"export": "導出到思源筆記成功"
},
diff --git a/src/renderer/src/pages/notes/utils/exportUtils.ts b/src/renderer/src/pages/notes/utils/exportUtils.ts
index 3bdf3972fa..fbce660856 100644
--- a/src/renderer/src/pages/notes/utils/exportUtils.ts
+++ b/src/renderer/src/pages/notes/utils/exportUtils.ts
@@ -1,5 +1,7 @@
import { loggerService } from '@logger'
import { RichEditorRef } from '@renderer/components/RichEditor/types'
+import i18n from '@renderer/i18n'
+import { getHighlighter } from '@renderer/utils/shiki'
import { RefObject } from 'react'
const logger = loggerService.withContext('exportUtils')
@@ -10,21 +12,85 @@ export interface ExportContext {
fileName: string
}
+/**
+ * 添加语法高亮
+ * @param html 原始 HTML
+ * @returns 添加语法高亮后的 HTML
+ */
+const addSyntaxHighlighting = async (html: string): Promise
=> {
+ try {
+ const highlighter = await getHighlighter()
+ const parser = new DOMParser()
+ const doc = parser.parseFromString(html, 'text/html')
+
+ const codeBlocks = doc.querySelectorAll('pre code')
+
+ for (const codeElement of codeBlocks) {
+ const preElement = codeElement.parentElement as HTMLPreElement
+ const codeText = codeElement.textContent || ''
+
+ if (!codeText.trim()) continue
+
+ let languageMatch = preElement.className.match(/language-(\w+)/)
+ if (!languageMatch) {
+ languageMatch = codeElement.className.match(/language-(\w+)/)
+ }
+ const language = languageMatch ? languageMatch[1] : 'text'
+
+ // Skip highlighting for plain text
+ if (language === 'text' || language === 'plain' || language === 'plaintext') {
+ continue
+ }
+
+ try {
+ const loadedLanguages = highlighter.getLoadedLanguages()
+
+ if (loadedLanguages.includes(language)) {
+ const highlightedHtml = highlighter.codeToHtml(codeText, {
+ lang: language,
+ theme: 'one-light'
+ })
+
+ const tempDoc = parser.parseFromString(highlightedHtml, 'text/html')
+ const highlightedCode = tempDoc.querySelector('code')
+
+ if (highlightedCode) {
+ codeElement.innerHTML = highlightedCode.innerHTML
+ }
+ }
+ } catch (error) {
+ logger.warn(`Failed to highlight ${language} code block:`, error as Error)
+ }
+ }
+
+ return doc.documentElement.outerHTML
+ } catch (error) {
+ logger.warn('Failed to add syntax highlighting, using original HTML:', error as Error)
+ return html
+ }
+}
+
export const handleExportPDF = async (context: ExportContext) => {
if (!context.editorRef?.current || !context.currentContent?.trim()) {
return
}
try {
- // Use Tiptap's getHTML API to get HTML content
- const htmlContent = context.editorRef.current.getHtml()
+ let htmlContent = context.editorRef.current.getHtml()
+
+ htmlContent = await addSyntaxHighlighting(htmlContent)
+
const filename = context.fileName ? `${context.fileName}.pdf` : 'note.pdf'
const result = await window.api.export.toPDF(htmlContent, filename)
if (result.success) {
logger.info('PDF exported successfully to:', result.filePath)
+ window.toast.success(i18n.t('message.success.pdf.export'))
} else {
logger.error('PDF export failed:', result.message)
+ if (!result.message.includes('canceled')) {
+ window.toast.error(i18n.t('message.error.pdf.export', { message: result.message }))
+ }
}
} catch (error: any) {
logger.error('PDF export error:', error)