You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Allow printing and creating PDF from iframe
This commit is contained in:
		| @@ -1,9 +1,88 @@ | ||||
| const { _ } = require('lib/locale'); | ||||
| const { bridge } = require('electron').remote.require('./bridge'); | ||||
| const InteropService = require('lib/services/InteropService'); | ||||
| const Setting = require('lib/models/Setting'); | ||||
| const md5 = require('md5'); | ||||
| const url = require('url'); | ||||
| const { shim } = require('lib/shim'); | ||||
| // const { BrowserWindow } = require('electron'); | ||||
|  | ||||
| class InteropServiceHelper { | ||||
|  | ||||
| 	static async exportNoteToHtmlFile(noteId) { | ||||
| 		const tempFile = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.html`; | ||||
| 		const exportOptions = {}; | ||||
| 		exportOptions.path = tempFile; | ||||
| 		exportOptions.format = 'html'; | ||||
| 		exportOptions.target = 'file'; | ||||
| 		exportOptions.sourceNoteIds = [noteId]; | ||||
|  | ||||
| 		const service = new InteropService(); | ||||
|  | ||||
| 		const result = await service.export(exportOptions); | ||||
| 		console.info('Export HTML result: ', result); | ||||
| 		return tempFile; | ||||
| 	} | ||||
|  | ||||
| 	static async exportNoteTo_(target, noteId, options = {}) { | ||||
| 		let win = null; | ||||
| 		let htmlFile = null; | ||||
|  | ||||
| 		const cleanup = () => { | ||||
| 			if (win) win.destroy(); | ||||
| 			if (htmlFile) shim.fsDriver().remove(htmlFile); | ||||
| 		}; | ||||
|  | ||||
| 		try { | ||||
| 			htmlFile = await this.exportNoteToHtmlFile(noteId); | ||||
|  | ||||
| 			const windowOptions = { | ||||
| 				show: true, | ||||
| 			}; | ||||
|  | ||||
| 			win = bridge().newBrowserWindow(windowOptions); | ||||
|  | ||||
| 			return new Promise((resolve, reject) => { | ||||
| 				win.webContents.on('did-finish-load', () => { | ||||
|  | ||||
| 					if (target === 'pdf') { | ||||
| 						win.webContents.printToPDF(options, (error, data) => { | ||||
| 							cleanup(); | ||||
| 							if (error) reject(error); | ||||
| 							resolve(data); | ||||
| 						}); | ||||
| 					} else { | ||||
| 						win.webContents.print(options, (success) => { | ||||
| 							// TODO: This is correct but broken in Electron 4. Need to upgrade to 5+ | ||||
| 							// It calls the callback right away with "false" even if the document hasn't be print yet. | ||||
|  | ||||
| 							cleanup(); | ||||
| 							if (!success) reject(new Error('Could not print')); | ||||
| 							resolve(); | ||||
| 						}); | ||||
| 					} | ||||
| 				}); | ||||
|  | ||||
| 				win.loadURL(url.format({ | ||||
| 					pathname: htmlFile, | ||||
| 					protocol: 'file:', | ||||
| 					slashes: true, | ||||
| 				})); | ||||
| 			}); | ||||
| 		} catch (error) { | ||||
| 			cleanup(); | ||||
| 			throw error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	static async exportNoteToPdf(noteId, options = {}) { | ||||
| 		return this.exportNoteTo_('pdf', noteId, options); | ||||
| 	} | ||||
|  | ||||
| 	static async printNote(noteId, options = {}) { | ||||
| 		return this.exportNoteTo_('printer', noteId, options); | ||||
| 	} | ||||
|  | ||||
| 	static async export(dispatch, module, options = null) { | ||||
| 		if (!options) options = {}; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const { _, setLocale } = require('lib/locale.js'); | ||||
| const { dirname } = require('lib/path-utils.js'); | ||||
| const { BrowserWindow } = require('electron'); | ||||
|  | ||||
| class Bridge { | ||||
|  | ||||
| @@ -21,6 +22,10 @@ class Bridge { | ||||
| 		return this.electronWrapper_.window(); | ||||
| 	} | ||||
|  | ||||
| 	newBrowserWindow(options) { | ||||
| 		return new BrowserWindow(options); | ||||
| 	} | ||||
|  | ||||
| 	windowContentSize() { | ||||
| 		if (!this.window()) return { width: 0, height: 0 }; | ||||
| 		const s = this.window().getContentSize(); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ const Folder = require('lib/models/Folder.js'); | ||||
| const Tag = require('lib/models/Tag.js'); | ||||
| const { time } = require('lib/time-utils.js'); | ||||
| const Setting = require('lib/models/Setting.js'); | ||||
| const InteropServiceHelper = require('../InteropServiceHelper.js'); | ||||
| const { IconButton } = require('./IconButton.min.js'); | ||||
| const { urlDecode, substrWithEllipsis } = require('lib/string-utils'); | ||||
| const Toolbar = require('./Toolbar.min.js'); | ||||
| @@ -942,6 +943,8 @@ class NoteTextComponent extends React.Component { | ||||
| 	} | ||||
|  | ||||
| 	webview_domReady() { | ||||
|  | ||||
| 		console.info('webview_domReady', this.webviewRef_.current); | ||||
| 		if (!this.webviewRef_.current) return; | ||||
|  | ||||
| 		this.setState({ | ||||
| @@ -1224,11 +1227,6 @@ class NoteTextComponent extends React.Component { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// helper function to style the title for printing | ||||
| 	title_(title) { | ||||
| 		return `<div style="font-size: 2em; font-weight: bold; border-bottom: 1px solid rgb(230,230,230); padding-bottom: .3em;">${title}</div><br>`; | ||||
| 	} | ||||
|  | ||||
| 	async printTo_(target, options) { | ||||
| 		if (this.props.selectedNoteIds.length !== 1 || !this.webviewRef_.current) { | ||||
| 			throw new Error(_('Only one note can be printed or exported to PDF at a time.')); | ||||
| @@ -1240,36 +1238,50 @@ class NoteTextComponent extends React.Component { | ||||
| 		} | ||||
|  | ||||
| 		this.isPrinting_ = true; | ||||
| 		const previousBody = this.state.note.body; | ||||
| 		const tempBody = `${this.title_(this.state.note.title)}\n\n${previousBody}`; | ||||
|  | ||||
| 		const previousTheme = Setting.value('theme'); | ||||
| 		Setting.setValue('theme', Setting.THEME_LIGHT); | ||||
| 		this.lastSetHtml_ = ''; | ||||
| 		await this.updateHtml(this.state.note.markup_language, tempBody, { useCustomCss: true }); | ||||
| 		this.forceUpdate(); | ||||
| 		// const previousBody = this.state.note.body; | ||||
| 		// const tempBody = `${this.state.note.title}\n\n${previousBody}`; | ||||
|  | ||||
| 		const restoreSettings = async () => { | ||||
| 			Setting.setValue('theme', previousTheme); | ||||
| 			this.lastSetHtml_ = ''; | ||||
| 			await this.updateHtml(this.state.note.markup_language, previousBody); | ||||
| 			this.forceUpdate(); | ||||
| 		}; | ||||
| 		// const previousTheme = Setting.value('theme'); | ||||
| 		// Setting.setValue('theme', Setting.THEME_LIGHT); | ||||
| 		// this.lastSetHtml_ = ''; | ||||
| 		// await this.updateHtml(this.state.note.markup_language, tempBody, { useCustomCss: true }); | ||||
| 		// this.forceUpdate(); | ||||
|  | ||||
| 		setTimeout(() => { | ||||
| 		// const restoreSettings = async () => { | ||||
| 		// 	Setting.setValue('theme', previousTheme); | ||||
| 		// 	this.lastSetHtml_ = ''; | ||||
| 		// 	await this.updateHtml(this.state.note.markup_language, previousBody); | ||||
| 		// 	this.forceUpdate(); | ||||
| 		// }; | ||||
|  | ||||
| 		// Need to save because the interop service reloads the note from the database | ||||
| 		await this.saveIfNeeded(); | ||||
|  | ||||
| 		setTimeout(async () => { | ||||
| 			if (target === 'pdf') { | ||||
| 				this.webviewRef_.current.wrappedInstance.printToPDF({ printBackground: true, pageSize: Setting.value('export.pdfPageSize'), landscape: Setting.value('export.pdfPageOrientation') === 'landscape' }, (error, data) => { | ||||
| 					restoreSettings(); | ||||
|  | ||||
| 					if (error) { | ||||
| 						bridge().showErrorMessageBox(error.message); | ||||
| 					} else { | ||||
| 						shim.fsDriver().writeFile(options.path, data, 'buffer'); | ||||
| 					} | ||||
| 				}); | ||||
| 				try { | ||||
| 					const pdfData = await InteropServiceHelper.exportNoteToPdf(this.state.note.id, { | ||||
| 						printBackground: true, | ||||
| 						pageSize: Setting.value('export.pdfPageSize'), | ||||
| 						landscape: Setting.value('export.pdfPageOrientation') === 'landscape', | ||||
| 					}); | ||||
| 					shim.fsDriver().writeFile(options.path, pdfData, 'buffer'); | ||||
| 				} catch (error) { | ||||
| 					console.error(error); | ||||
| 					bridge().showErrorMessageBox(error.message); | ||||
| 				} | ||||
| 			} else if (target === 'printer') { | ||||
| 				this.webviewRef_.current.wrappedInstance.print({ printBackground: true }); | ||||
| 				restoreSettings(); | ||||
| 				try { | ||||
| 					await InteropServiceHelper.printNote(this.state.note.id, { | ||||
| 						printBackground: true, | ||||
| 					}); | ||||
| 				} catch (error) { | ||||
| 					console.error(error); | ||||
| 					bridge().showErrorMessageBox(error.message); | ||||
| 				} | ||||
|  | ||||
| 				// restoreSettings(); | ||||
| 			} | ||||
| 			this.isPrinting_ = false; | ||||
| 		}, 100); | ||||
|   | ||||
| @@ -5,10 +5,6 @@ class NoteTextViewerComponent extends React.Component { | ||||
| 	constructor() { | ||||
| 		super(); | ||||
|  | ||||
| 		this.state = { | ||||
| 			'html': '', | ||||
| 		}; | ||||
|  | ||||
| 		this.initialized_ = false; | ||||
| 		this.domReady_ = false; | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,7 @@ const MarkupToHtml = require('lib/renderers/MarkupToHtml.js'); | ||||
| const dataurl	 = require('dataurl'); | ||||
| const { themeStyle } = require('../../theme.js'); | ||||
| const { dirname } = require('lib/path-utils.js'); | ||||
| const { escapeHtml } = require('lib/string-utils.js'); | ||||
|  | ||||
| class InteropService_Exporter_Html extends InteropService_Exporter_Base { | ||||
|  | ||||
| @@ -104,10 +105,23 @@ class InteropService_Exporter_Html extends InteropService_Exporter_Base { | ||||
| 			const bodyMd = await this.processNoteResources_(item); | ||||
| 			const result = this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { resources: this.resources_, plainResourceRendering: true }); | ||||
| 			const noteContent = []; | ||||
| 			if (item.title) noteContent.push(`<div class="exported-note-title">${item.title}</div>`); | ||||
| 			if (item.title) noteContent.push(`<div class="exported-note-title">${escapeHtml(item.title)}</div>`); | ||||
| 			if (result.html) noteContent.push(result.html); | ||||
|  | ||||
| 			await shim.fsDriver().writeFile(noteFilePath, `<div class="exported-note">${noteContent.join('\n\n')}</div>`, 'utf-8'); | ||||
| 			const fullHtml = ` | ||||
| 				<!DOCTYPE html> | ||||
| 				<html> | ||||
| 					<head> | ||||
| 						<meta charset="UTF-8"> | ||||
| 						<title>${escapeHtml(item.title)}</title> | ||||
| 					</head> | ||||
| 					<body> | ||||
| 						<div class="exported-note">${noteContent.join('\n\n')}</div> | ||||
| 					</body> | ||||
| 				</html> | ||||
| 			`; | ||||
|  | ||||
| 			await shim.fsDriver().writeFile(noteFilePath, fullHtml, 'utf-8'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user