mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Allow printing and creating PDF from iframe
This commit is contained in:
parent
f10695fb8f
commit
ee38590c35
@ -1,9 +1,88 @@
|
|||||||
const { _ } = require('lib/locale');
|
const { _ } = require('lib/locale');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const InteropService = require('lib/services/InteropService');
|
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 {
|
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) {
|
static async export(dispatch, module, options = null) {
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const { _, setLocale } = require('lib/locale.js');
|
const { _, setLocale } = require('lib/locale.js');
|
||||||
const { dirname } = require('lib/path-utils.js');
|
const { dirname } = require('lib/path-utils.js');
|
||||||
|
const { BrowserWindow } = require('electron');
|
||||||
|
|
||||||
class Bridge {
|
class Bridge {
|
||||||
|
|
||||||
@ -21,6 +22,10 @@ class Bridge {
|
|||||||
return this.electronWrapper_.window();
|
return this.electronWrapper_.window();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newBrowserWindow(options) {
|
||||||
|
return new BrowserWindow(options);
|
||||||
|
}
|
||||||
|
|
||||||
windowContentSize() {
|
windowContentSize() {
|
||||||
if (!this.window()) return { width: 0, height: 0 };
|
if (!this.window()) return { width: 0, height: 0 };
|
||||||
const s = this.window().getContentSize();
|
const s = this.window().getContentSize();
|
||||||
|
@ -7,6 +7,7 @@ const Folder = require('lib/models/Folder.js');
|
|||||||
const Tag = require('lib/models/Tag.js');
|
const Tag = require('lib/models/Tag.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const Setting = require('lib/models/Setting.js');
|
||||||
|
const InteropServiceHelper = require('../InteropServiceHelper.js');
|
||||||
const { IconButton } = require('./IconButton.min.js');
|
const { IconButton } = require('./IconButton.min.js');
|
||||||
const { urlDecode, substrWithEllipsis } = require('lib/string-utils');
|
const { urlDecode, substrWithEllipsis } = require('lib/string-utils');
|
||||||
const Toolbar = require('./Toolbar.min.js');
|
const Toolbar = require('./Toolbar.min.js');
|
||||||
@ -942,6 +943,8 @@ class NoteTextComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
webview_domReady() {
|
webview_domReady() {
|
||||||
|
|
||||||
|
console.info('webview_domReady', this.webviewRef_.current);
|
||||||
if (!this.webviewRef_.current) return;
|
if (!this.webviewRef_.current) return;
|
||||||
|
|
||||||
this.setState({
|
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) {
|
async printTo_(target, options) {
|
||||||
if (this.props.selectedNoteIds.length !== 1 || !this.webviewRef_.current) {
|
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.'));
|
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;
|
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');
|
// const previousBody = this.state.note.body;
|
||||||
Setting.setValue('theme', Setting.THEME_LIGHT);
|
// const tempBody = `${this.state.note.title}\n\n${previousBody}`;
|
||||||
this.lastSetHtml_ = '';
|
|
||||||
await this.updateHtml(this.state.note.markup_language, tempBody, { useCustomCss: true });
|
|
||||||
this.forceUpdate();
|
|
||||||
|
|
||||||
const restoreSettings = async () => {
|
// const previousTheme = Setting.value('theme');
|
||||||
Setting.setValue('theme', previousTheme);
|
// Setting.setValue('theme', Setting.THEME_LIGHT);
|
||||||
this.lastSetHtml_ = '';
|
// this.lastSetHtml_ = '';
|
||||||
await this.updateHtml(this.state.note.markup_language, previousBody);
|
// await this.updateHtml(this.state.note.markup_language, tempBody, { useCustomCss: true });
|
||||||
this.forceUpdate();
|
// 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') {
|
if (target === 'pdf') {
|
||||||
this.webviewRef_.current.wrappedInstance.printToPDF({ printBackground: true, pageSize: Setting.value('export.pdfPageSize'), landscape: Setting.value('export.pdfPageOrientation') === 'landscape' }, (error, data) => {
|
try {
|
||||||
restoreSettings();
|
const pdfData = await InteropServiceHelper.exportNoteToPdf(this.state.note.id, {
|
||||||
|
printBackground: true,
|
||||||
if (error) {
|
pageSize: Setting.value('export.pdfPageSize'),
|
||||||
bridge().showErrorMessageBox(error.message);
|
landscape: Setting.value('export.pdfPageOrientation') === 'landscape',
|
||||||
} else {
|
|
||||||
shim.fsDriver().writeFile(options.path, data, 'buffer');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
shim.fsDriver().writeFile(options.path, pdfData, 'buffer');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
bridge().showErrorMessageBox(error.message);
|
||||||
|
}
|
||||||
} else if (target === 'printer') {
|
} else if (target === 'printer') {
|
||||||
this.webviewRef_.current.wrappedInstance.print({ printBackground: true });
|
try {
|
||||||
restoreSettings();
|
await InteropServiceHelper.printNote(this.state.note.id, {
|
||||||
|
printBackground: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
bridge().showErrorMessageBox(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restoreSettings();
|
||||||
}
|
}
|
||||||
this.isPrinting_ = false;
|
this.isPrinting_ = false;
|
||||||
}, 100);
|
}, 100);
|
||||||
|
@ -5,10 +5,6 @@ class NoteTextViewerComponent extends React.Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
|
||||||
'html': '',
|
|
||||||
};
|
|
||||||
|
|
||||||
this.initialized_ = false;
|
this.initialized_ = false;
|
||||||
this.domReady_ = false;
|
this.domReady_ = false;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ const MarkupToHtml = require('lib/renderers/MarkupToHtml.js');
|
|||||||
const dataurl = require('dataurl');
|
const dataurl = require('dataurl');
|
||||||
const { themeStyle } = require('../../theme.js');
|
const { themeStyle } = require('../../theme.js');
|
||||||
const { dirname } = require('lib/path-utils.js');
|
const { dirname } = require('lib/path-utils.js');
|
||||||
|
const { escapeHtml } = require('lib/string-utils.js');
|
||||||
|
|
||||||
class InteropService_Exporter_Html extends InteropService_Exporter_Base {
|
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 bodyMd = await this.processNoteResources_(item);
|
||||||
const result = this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { resources: this.resources_, plainResourceRendering: true });
|
const result = this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { resources: this.resources_, plainResourceRendering: true });
|
||||||
const noteContent = [];
|
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);
|
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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user