Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export a Markdown note as PDF #2678

Merged
merged 12 commits into from
Mar 20, 2019
120 changes: 71 additions & 49 deletions browser/components/MarkdownPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export default class MarkdownPreview extends React.Component {
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
this.saveAsPdfHandler = () => this.handleSaveAsPdf()
this.printHandler = () => this.handlePrint()

this.linkClickHandler = this.handlelinkClick.bind(this)
Expand Down Expand Up @@ -294,58 +295,77 @@ export default class MarkdownPreview extends React.Component {
this.exportAsDocument('md')
}

handleSaveAsHtml () {
this.exportAsDocument('html', (noteContent, exportTasks) => {
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()

const inlineStyles = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
let body = this.markdown.render(noteContent)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
files.forEach(file => {
if (global.process.platform === 'win32') {
file = file.replace('file:///', '')
} else {
file = file.replace('file://', '')
}
exportTasks.push({
src: file,
dst: 'css'
})
})
htmlContentFormatter (noteContent, exportTasks, targetDir) {
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()

let styles = ''
files.forEach(file => {
styles += `<link rel="stylesheet" href="css/${path.basename(file)}">`
const inlineStyles = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
let body = this.markdown.render(noteContent)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
files.forEach(file => {
if (global.process.platform === 'win32') {
file = file.replace('file:///', '')
} else {
file = file.replace('file://', '')
}
exportTasks.push({
src: file,
dst: 'css'
})
})

return `<html>
<head>
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width, initial-scale = 1, maximum-scale = 1">
<style id="style">${inlineStyles}</style>
${styles}
</head>
<body>${body}</body>
</html>`
let styles = ''
files.forEach(file => {
styles += `<link rel="stylesheet" href="css/${path.basename(file)}">`
})

return `<html>
<head>
<base href="file://${targetDir}/">
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width, initial-scale = 1, maximum-scale = 1">
<style id="style">${inlineStyles}</style>
${styles}
</head>
<body>${body}</body>
</html>`
}

handleSaveAsHtml () {
this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks, targetDir)))
}

handleSaveAsPdf () {
this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => {
const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false}})
printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir))
return new Promise((resolve, reject) => {
printout.webContents.on('did-finish-load', () => {
printout.webContents.printToPDF({}, (err, data) => {
if (err) reject(err)
else resolve(data)
AgentEpsilon marked this conversation as resolved.
Show resolved Hide resolved
printout.destroy()
})
})
})
})
}

Expand Down Expand Up @@ -460,6 +480,7 @@ export default class MarkdownPreview extends React.Component {
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
eventEmitter.on('export:save-pdf', this.saveAsPdfHandler)
eventEmitter.on('print', this.printHandler)
}

Expand Down Expand Up @@ -495,6 +516,7 @@ export default class MarkdownPreview extends React.Component {
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
eventEmitter.off('export:save-pdf', this.saveAsPdfHandler)
eventEmitter.off('print', this.printHandler)
}

Expand Down
8 changes: 7 additions & 1 deletion browser/main/Detail/InfoPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class InfoPanel extends React.Component {

render () {
const {
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, exportAsPdf, wordCount, letterCount, type, print
} = this.props
return (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
Expand Down Expand Up @@ -85,6 +85,11 @@ class InfoPanel extends React.Component {
<p>{i18n.__('.html')}</p>
</button>

<button styleName='export--enable' onClick={(e) => exportAsPdf(e, 'export-pdf')}>
<i className='fa fa-file-pdf-o' />
<p>{i18n.__('.pdf')}</p>
</button>

<button styleName='export--enable' onClick={(e) => print(e, 'print')}>
<i className='fa fa-print' />
<p>{i18n.__('Print')}</p>
Expand All @@ -104,6 +109,7 @@ InfoPanel.propTypes = {
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired,
wordCount: PropTypes.number,
letterCount: PropTypes.number,
type: PropTypes.string.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion browser/main/Detail/InfoPanel.styl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
right 25px
position absolute
padding 20px 25px 0 25px
width 300px
// width 300px
overflow auto
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
Expand Down
7 changes: 4 additions & 3 deletions browser/main/Detail/InfoPanelTrashed.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import styles from './InfoPanel.styl'
import i18n from 'browser/lib/i18n'

const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, exportAsPdf
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div>
Expand Down Expand Up @@ -46,7 +46,7 @@ const InfoPanelTrashed = ({
<p>.html</p>
</button>

<button styleName='export--unable'>
<button styleName='export--enable' onClick={(e) => exportAsPdf(e, 'export-pdf')}>
<i className='fa fa-file-pdf-o' />
<p>.pdf</p>
</button>
Expand All @@ -61,7 +61,8 @@ InfoPanelTrashed.propTypes = {
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired
}

export default CSSModules(InfoPanelTrashed, styles)
6 changes: 6 additions & 0 deletions browser/main/Detail/MarkdownNoteDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ class MarkdownNoteDetail extends React.Component {
ee.emit('export:save-html')
}

exportAsPdf () {
ee.emit('export:save-pdf')
}

handleKeyDown (e) {
switch (e.keyCode) {
// tab key
Expand Down Expand Up @@ -411,6 +415,7 @@ class MarkdownNoteDetail extends React.Component {
exportAsHtml={this.exportAsHtml}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsPdf={this.exportAsPdf}
/>
</div>
</div>
Expand Down Expand Up @@ -476,6 +481,7 @@ class MarkdownNoteDetail extends React.Component {
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsHtml={this.exportAsHtml}
exportAsPdf={this.exportAsPdf}
wordCount={note.content.split(' ').length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
Expand Down
3 changes: 3 additions & 0 deletions browser/main/Detail/SnippetNoteDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ class SnippetNoteDetail extends React.Component {
'export-txt': 'Text export',
'export-md': 'Markdown export',
'export-html': 'HTML export',
'export-pdf': 'PDF export',
'print': 'Print'
})[msg]

Expand Down Expand Up @@ -757,6 +758,7 @@ class SnippetNoteDetail extends React.Component {
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
exportAsHtml={this.showWarning}
exportAsPdf={this.showWarning}
/>
</div>
</div>
Expand Down Expand Up @@ -808,6 +810,7 @@ class SnippetNoteDetail extends React.Component {
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
exportAsHtml={this.showWarning}
exportAsPdf={this.showWarning}
type={note.type}
print={this.showWarning}
/>
Expand Down
1 change: 1 addition & 0 deletions browser/main/NoteList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ class NoteList extends React.Component {
'export-txt': 'Text export',
'export-md': 'Markdown export',
'export-html': 'HTML export',
'export-pdf': 'PDF export',
'print': 'Print'
})[msg]

Expand Down
11 changes: 5 additions & 6 deletions browser/main/lib/dataApi/exportFolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,18 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) {
.then(function exportNotes (data) {
const { storage, notes } = data

notes
return Promise.all(notes
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
.forEach(note => {
.map(note => {
const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`)
exportNote(note.key, storage.path, note.content, notePath, null)
return exportNote(note.key, storage.path, note.content, notePath, null)
})

return {
).then(() => ({
storage,
folderKey,
fileType,
exportDir
}
}))
})
}

Expand Down
9 changes: 6 additions & 3 deletions browser/main/lib/dataApi/exportNote.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatt
)

if (outputFormatter) {
exportedData = outputFormatter(exportedData, exportTasks)
exportedData = outputFormatter(exportedData, exportTasks, path.dirname(targetPath))
} else {
exportedData = Promise.resolve(exportedData)
}

const tasks = prepareTasks(exportTasks, storagePath, path.dirname(targetPath))

return Promise.all(tasks.map((task) => copyFile(task.src, task.dst)))
.then(() => {
return saveToFile(exportedData, targetPath)
.then(() => exportedData)
.then(data => {
return saveToFile(data, targetPath)
}).catch((err) => {
rollbackExport(tasks)
throw err
Expand Down
7 changes: 7 additions & 0 deletions lib/main-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ const file = {
mainWindow.webContents.send('list:isMarkdownNote', 'export-html')
mainWindow.webContents.send('export:save-html')
}
},
{
label: 'PDF (.pdf)',
click () {
mainWindow.webContents.send('list:isMarkdownNote', 'export-pdf')
mainWindow.webContents.send('export:save-pdf')
}
}
]
},
Expand Down
1 change: 1 addition & 0 deletions locales/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storage Locations": "Storage Locations",
Expand Down
1 change: 1 addition & 0 deletions locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Drucken",
"Your preferences for Boostnote": "Boostnote Einstellungen",
"Storage Locations": "Speicherverwaltung",
Expand Down
1 change: 1 addition & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Help": "Help",
Expand Down
1 change: 1 addition & 0 deletions locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Imprimir",
"Your preferences for Boostnote": "Tus preferencias para Boostnote",
"Storage Locations": "Almacenamientos",
Expand Down
1 change: 1 addition & 0 deletions locales/fa.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "پرینت",
"Your preferences for Boostnote": "تنظیمات شما برای boostnote",
"Storage Locations": "ذخیره سازی",
Expand Down
1 change: 1 addition & 0 deletions locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Imprimer",
"Your preferences for Boostnote": "Vos préférences pour Boostnote",
"Storage Locations": "Stockages",
Expand Down
1 change: 1 addition & 0 deletions locales/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Nyomtatás",
"Your preferences for Boostnote": "Boostnote beállításaid",
"Help": "Súgó",
Expand Down
1 change: 1 addition & 0 deletions locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
".md": ".md",
".txt": ".txt",
".html": ".html",
".pdf": ".pdf",
"Print": "Stampa",
"Your preferences for Boostnote": "Le tue preferenze per Boostnote",
"Storage Locations": "Posizioni",
Expand Down
Loading