feat: enhance PDF export functionality with syntax highlighting and localized messages

This commit is contained in:
suyao 2025-09-12 07:53:26 +08:00
parent 60e69ad49c
commit a17d62f3ec
No known key found for this signature in database
5 changed files with 88 additions and 4 deletions

View File

@ -29,8 +29,8 @@ export const PRINT_HTML_TEMPLATE = `
{{richtextCss}}
</style>
</head>
<body>
<div id="root" theme-mode="light" os=${isMac ? 'mac' : isWin ? 'windows' : 'linux'}>
<body theme-mode="light" os=${isMac ? 'mac' : isWin ? 'windows' : 'linux'}>
<div id="root">
<div class="tiptap">{{content}}</div>
</div>
</body>

View File

@ -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"
},

View File

@ -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": "导出到思源笔记成功"
},

View File

@ -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": "導出到思源筆記成功"
},

View File

@ -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<string> => {
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)