Skip to content

Commit 8f58d01

Browse files
committed
Fix pdf saving
1 parent b467342 commit 8f58d01

File tree

5 files changed

+127
-95
lines changed

5 files changed

+127
-95
lines changed

src/components/organisms/NotePageToolbar.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,6 @@ const NotePageToolbar = ({
216216
note,
217217
preferences,
218218
pushMessage,
219-
{
220-
includeFrontMatter,
221-
},
222219
previewStyle
223220
),
224221
},

src/lib/electronOnly.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MenuItemConstructorOptions,
77
IpcRendererEvent,
88
WebContents,
9+
PrintToPDFOptions,
910
} from 'electron'
1011

1112
const __ELECTRON_ONLY__: {
@@ -46,6 +47,10 @@ const __ELECTRON_ONLY__: {
4647
isDefaultProtocolClient(protocol: string): boolean
4748
getWebContentsById(id: number): WebContents
4849
setTrafficLightPosition(position: { x: number; y: number }): void
50+
convertHtmlStringToPdfBlob(
51+
htmlString: string,
52+
printOptions: PrintToPDFOptions
53+
): Promise<Blob>
4954
} = (window as any).__ELECTRON_ONLY__
5055

5156
const {
@@ -72,6 +77,7 @@ const {
7277
isDefaultProtocolClient,
7378
getWebContentsById,
7479
setTrafficLightPosition,
80+
convertHtmlStringToPdfBlob,
7581
} = __ELECTRON_ONLY__ || {}
7682

7783
async function readFileAsString(pathname: string) {
@@ -123,4 +129,5 @@ export {
123129
isDefaultProtocolClient,
124130
getWebContentsById,
125131
setTrafficLightPosition,
132+
convertHtmlStringToPdfBlob,
126133
}

src/lib/exports.ts

Lines changed: 52 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import rehypeReact from 'rehype-react'
2020
import CodeFence from '../components/atoms/markdown/CodeFence'
2121
import { getGlobalCss, selectTheme } from './styled/styleUtil'
2222
import yaml from 'yaml'
23+
import { convertHtmlStringToPdfBlob } from './electronOnly'
2324

24-
const filenamifyNoteTitle = function (noteTitle: string): string {
25+
export function filenamifyNoteTitle(noteTitle: string): string {
2526
return filenamify(noteTitle.toLowerCase().replace(/\s+/g, '-'))
2627
}
2728

@@ -192,99 +193,59 @@ export const exportNoteAsPdfFile = async (
192193
note: NoteDoc,
193194
preferences: Preferences,
194195
pushMessage: (context: any) => any,
195-
{ includeFrontMatter }: { includeFrontMatter: boolean },
196196
previewStyle?: string
197197
): Promise<void> => {
198-
await unified()
199-
.use(remarkParse)
200-
.use(remarkEmoji, { emoticon: false })
201-
.use([remarkRehype, { allowDangerousHTML: true }])
202-
.use(rehypeRaw)
203-
.use(rehypeSanitize, schema)
204-
.use(remarkMath)
205-
.use(rehypeCodeMirror, {
206-
ignoreMissing: true,
207-
theme: preferences['markdown.codeBlockTheme'],
208-
})
209-
.use(rehypeKatex, { output: 'htmlAndMathml' })
210-
.use(rehypeReact, {
211-
createElement: React.createElement,
212-
components: {
213-
pre: CodeFence,
214-
},
215-
})
216-
.use(rehypeStringify)
217-
.process(note.content, (err, file) => {
218-
if (err != null) {
219-
pushMessage({
220-
title: 'Note processing failed',
221-
description: 'Please check markdown syntax and try again later.',
222-
})
223-
return
224-
}
225-
226-
const stringifiedMdContent = file.toString().trim() + '\n'
227-
const { BrowserWindow } = window.require('electron').remote
228-
const windowOptions = {
229-
webPreferences: {
230-
nodeIntegration: true,
231-
webSecurity: false,
232-
javascript: false,
198+
try {
199+
const output = await unified()
200+
.use(remarkParse)
201+
.use(remarkEmoji, { emoticon: false })
202+
.use([remarkRehype, { allowDangerousHTML: true }])
203+
.use(rehypeRaw)
204+
.use(rehypeSanitize, schema)
205+
.use(remarkMath)
206+
.use(rehypeCodeMirror, {
207+
ignoreMissing: true,
208+
theme: preferences['markdown.codeBlockTheme'],
209+
})
210+
.use(rehypeKatex, { output: 'htmlAndMathml' })
211+
.use(rehypeReact, {
212+
createElement: React.createElement,
213+
components: {
214+
pre: CodeFence,
233215
},
234-
show: false,
235-
}
236-
const win = new BrowserWindow(windowOptions)
237-
const htmlStr = generatePrintToPdfHTML(
238-
stringifiedMdContent,
239-
preferences,
240-
previewStyle
241-
)
242-
const encodedStr = encodeURIComponent(htmlStr)
243-
win.loadURL('data:text/html;charset=UTF-8,' + encodedStr)
244-
win.webContents.on('did-finish-load', function () {
245-
// Enable when newer version of electron is available
246-
const tagsStr =
247-
note.tags.length > 0 ? `, tags: [${note.tags.join(' ')}]` : ''
248-
const headerFooter: Record<string, string> = {
249-
title: `${note.title}${tagsStr}`,
250-
url: `file://${filenamifyNoteTitle(note.title)}.pdf`,
251-
}
252-
const printOpts = {
253-
// Needed for codemirorr themes (backgrounds)
254-
printBackground: true,
255-
// Enable margins if header footer is printed
256-
// No margins 1, default margins 0, 2 - minimum margins
257-
marginsType: includeFrontMatter ? 0 : 1,
258-
pageSize: 'A4', // This could be chosen by user,
259-
headerFooter: includeFrontMatter ? headerFooter : undefined,
260-
}
261-
win.webContents
262-
.printToPDF(printOpts)
263-
.then((data) => {
264-
if (data) {
265-
// We got the PDF - offer the user to save it
266-
const pdfName = `${filenamifyNoteTitle(note.title)}.pdf`
267-
const pdfBlob = new Blob([data], {
268-
type: 'application/pdf', // application/octet-stream
269-
})
270-
downloadBlob(pdfBlob, pdfName)
271-
} else {
272-
pushMessage({
273-
title: 'PDF export failed',
274-
description: 'Please try again later. Reason: Unknown',
275-
})
276-
}
277-
// Destroy window (not shown but disposes it)
278-
win.destroy()
279-
})
280-
.catch((err) => {
281-
pushMessage({
282-
title: 'PDF export failed',
283-
description: 'Please try again later.' + (err ? err : 'Unknown'),
284-
})
285-
})
286216
})
287-
return
217+
.use(rehypeStringify)
218+
.process(note.content)
219+
const markdownContent = output.toString('utf-8').trim() + '\n'
220+
const htmlString = generatePrintToPdfHTML(
221+
markdownContent,
222+
preferences,
223+
previewStyle
224+
)
225+
226+
// Enable when newer version of electron is available
227+
// const tagsStr =
228+
// note.tags.length > 0 ? `, tags: [${note.tags.join(' ')}]` : ''
229+
// const headerFooter: Record<string, string> = {
230+
// title: `${note.title}${tagsStr}`,
231+
// url: `file://${filenamifyNoteTitle(note.title)}.pdf`,
232+
// }
233+
const printOpts = {
234+
// Needed for codemirorr themes (backgrounds)
235+
printBackground: true,
236+
// Enable margins if header footer is printed
237+
// No margins 1, default margins 0, 2 - minimum margins
238+
// marginsType: includeFrontMatter ? 0 : 1,
239+
pageSize: 'A4', // This could be chosen by user,
240+
// headerFooter: includeFrontMatter ? headerFooter : undefined,
241+
}
242+
const pdfBlob = await convertHtmlStringToPdfBlob(htmlString, printOpts)
243+
const pdfName = `${filenamifyNoteTitle(note.title)}.pdf`
244+
downloadBlob(pdfBlob, pdfName)
245+
} catch (error) {
246+
pushMessage({
247+
title: 'PDF export failed',
248+
description: error.message,
288249
})
289-
return
250+
}
290251
}

static/boosthub-preload.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,42 @@
11
/* eslint-disable @typescript-eslint/no-var-requires */
2-
const { ipcRenderer } = require('electron')
2+
const { ipcRenderer, remote } = require('electron')
33

44
function sendToHost(channel, ...args) {
55
ipcRenderer.sendToHost(channel, ...args)
66
}
7+
8+
function convertHtmlStringToPdfBlob(htmlString, printOptions) {
9+
return new Promise((resolve, reject) => {
10+
const encodedStr = encodeURIComponent(htmlString)
11+
const { BrowserWindow } = remote
12+
const windowOptions = {
13+
webPreferences: {
14+
nodeIntegration: false,
15+
webSecurity: false,
16+
javascript: false,
17+
},
18+
show: false,
19+
}
20+
const browserWindow = new BrowserWindow(windowOptions)
21+
browserWindow.loadURL('data:text/html;charset=UTF-8,' + encodedStr)
22+
23+
browserWindow.webContents.on('did-finish-load', async () => {
24+
try {
25+
const pdfFileBuffer = await browserWindow.webContents.printToPDF(
26+
printOptions
27+
)
28+
const blob = new Blob([pdfFileBuffer], {
29+
type: 'application/pdf',
30+
})
31+
resolve(blob)
32+
} catch (error) {
33+
reject(error)
34+
} finally {
35+
browserWindow.destroy()
36+
}
37+
})
38+
})
39+
}
740
window.__ELECTRON_ONLY__ = {}
841
window.__ELECTRON_ONLY__.sendToHost = sendToHost
42+
window.__ELECTRON_ONLY__.convertHtmlStringToPdfBlob = convertHtmlStringToPdfBlob

static/main-preload.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,39 @@
157157
electron.remote.getCurrentWindow().setTrafficLightPosition(position)
158158
}
159159

160+
function convertHtmlStringToPdfBlob(htmlString, printOptions) {
161+
return new Promise((resolve, reject) => {
162+
const encodedStr = encodeURIComponent(htmlString)
163+
const { BrowserWindow } = remote
164+
const windowOptions = {
165+
webPreferences: {
166+
nodeIntegration: false,
167+
webSecurity: false,
168+
javascript: false,
169+
},
170+
show: false,
171+
}
172+
const browserWindow = new BrowserWindow(windowOptions)
173+
browserWindow.loadURL('data:text/html;charset=UTF-8,' + encodedStr)
174+
175+
browserWindow.webContents.on('did-finish-load', async () => {
176+
try {
177+
const pdfFileBuffer = await browserWindow.webContents.printToPDF(
178+
printOptions
179+
)
180+
const blob = new Blob([pdfFileBuffer], {
181+
type: 'application/pdf',
182+
})
183+
resolve(blob)
184+
} catch (error) {
185+
reject(error)
186+
} finally {
187+
browserWindow.destroy()
188+
}
189+
})
190+
})
191+
}
192+
160193
window.__ELECTRON_ONLY__ = {}
161194
window.__ELECTRON_ONLY__.openExternal = openExternal
162195
window.__ELECTRON_ONLY__.readFile = readFile

0 commit comments

Comments
 (0)