2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

PDF generation

- /api/method/pdf
- frappe.getPDF
- download using blob url
This commit is contained in:
Faris Ansari 2018-04-15 00:44:02 +05:30
parent 7d3cb0cfbb
commit 32cd6581d1
12 changed files with 258 additions and 56 deletions

View File

@ -1,5 +1,6 @@
const Page = require('frappejs/client/view/page');
const frappe = require('frappejs');
const Page = require('frappejs/client/view/page');
const { getHTML } = require('frappejs/common/print');
const nunjucks = require('nunjucks/browser/nunjucks');
nunjucks.configure({ autoescape: false });
@ -16,12 +17,8 @@ module.exports = class PrintPage extends Page {
frappe.router.setRoute('edit', this.doctype, this.name)
});
this.addButton(frappe._('Print'), 'secondary', async () => {
const pdf = require('frappejs/server/pdf');
const savePath = '/Users/farisansari/frappe.pdf';
pdf(await this.getHTML(true), savePath);
const { shell } = require('electron');
shell.openItem(savePath);
this.addButton(frappe._('PDF'), 'secondary', async () => {
frappe.getPDF(this.doctype, this.name);
});
}
@ -37,28 +34,15 @@ module.exports = class PrintPage extends Page {
}
async renderTemplate() {
let doc = await frappe.getDoc(this.doctype, this.name);
frappe.desk.setActiveDoc(doc);
const html = await getHTML(this.doctype, this.name);
try {
this.body.innerHTML = await this.getHTML();
this.body.innerHTML = html;
// this.setTitle(doc.name);
} catch (e) {
this.renderError('Template Error', e);
throw e;
}
}
async getHTML(pdf = false) {
this.printFormat = await frappe.getDoc('PrintFormat', this.meta.print.printFormat);
let doc = await frappe.getDoc(this.doctype, this.name);
let context = {doc: doc, frappe: frappe};
frappe.desk.setActiveDoc(doc);
return `
${pdf ? `
<style>
${require('fs').readFileSync('./www/dist/css/style.css').toString()}
</style>
` : ''}
<div class="print-page">
${nunjucks.renderString(this.printFormat.template, context)}
</div>`;
}
}

View File

@ -4,6 +4,7 @@ const frappe = require('frappejs');
frappe.ui = require('./ui');
const Desk = require('./desk');
const Observable = require('frappejs/utils/observable');
const { getPDF } = require('frappejs/client/pdf');
module.exports = {
async start({server, columns = 2, makeDesk = 1}) {
@ -47,6 +48,8 @@ module.exports = {
return await response.json();
}
frappe.getPDF = getPDF;
}
};

30
client/pdf.js Normal file
View File

@ -0,0 +1,30 @@
async function getPDF(doctype, name) {
const headers = {
'Accept': 'application/pdf',
'Content-Type': 'application/json'
}
const res = await fetch('/api/method/pdf', {
method: 'POST',
headers,
body: JSON.stringify({ doctype, name })
});
const blob = await res.blob();
showFile(blob);
}
function showFile(blob, filename='file.pdf') {
const newBlob = new Blob([blob], { type: "application/pdf" })
const data = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = data;
link.download = filename;
link.click();
setTimeout(() => window.URL.revokeObjectURL(data), 100);
}
module.exports = {
getPDF
}

View File

@ -38,8 +38,9 @@ module.exports = class BaseTree extends BaseList {
this.body.innerHTML = '';
this.dirty = false;
let accountingSettings = await frappe.db.getSingle('AccountingSettings');
let rootLabel = accountingSettings.companyName;
const rootLabel = this.treeSettings.getRootLabel ?
await this.treeSettings.getRootLabel() :
this.doctype;
this.renderTree(rootLabel);
this.trigger('state-change');

29
common/print.js Normal file
View File

@ -0,0 +1,29 @@
const frappe = require('frappejs');
const nunjucks = require('nunjucks/browser/nunjucks');
async function getHTML(doctype, name) {
const meta = frappe.getMeta(doctype);
const printFormat = await frappe.getDoc('PrintFormat', meta.print.printFormat);
let doc = await frappe.getDoc(doctype, name);
let context = {doc: doc, frappe: frappe};
console.log(context);
let html;
try {
html = nunjucks.renderString(printFormat.template, context);
} catch (error) {
console.log(error);
html = '';
}
return `
<div class="print-page">
${html}
</div>
`;
}
module.exports = {
getHTML
}

View File

@ -4,11 +4,13 @@ module.exports = {
file: './www/dist/js/bundle.js',
format: 'iife',
name: 'desk',
globals: ['io', 'nunjucks'] // for socketio client, which is imported directly
sourcemap: true,
globals: ['io', 'nunjucks'], // for socketio client, which is imported directly,
},
plugins: [
require('rollup-plugin-commonjs')(),
require('rollup-plugin-json')(),
require('rollup-plugin-html')(),
require('rollup-plugin-node-resolve')({
preferBuiltins: true
}),

View File

@ -19,8 +19,8 @@
"eslint": "^4.19.1",
"express": "^4.16.2",
"flatpickr": "^4.3.2",
"frappejs": "../frappejs",
"frappe-datatable": "frappe/datatable",
"frappejs": "../frappejs",
"jquery": "^3.3.1",
"mkdirp": "^0.5.1",
"mocha": "^4.1.0",
@ -36,6 +36,7 @@
"puppeteer": "^1.2.0",
"rollup": "^0.55.1",
"rollup-plugin-commonjs": "^8.3.0",
"rollup-plugin-html": "^0.2.1",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-resolve": "^3.0.2",
"rollup-plugin-postcss": "^1.2.7",

View File

@ -12,6 +12,7 @@ const frappeModels = require('frappejs/models');
const common = require('frappejs/common');
const bodyParser = require('body-parser');
const fs = require('fs');
const { setupExpressRoute: setRouteForPDF } = require('frappejs/server/pdf');
require.extensions['.html'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8');
@ -19,6 +20,7 @@ require.extensions['.html'] = function (module, filename) {
module.exports = {
async start({backend, connectionParams, models, staticPath = './'}) {
await this.init();
if (models) {
@ -41,10 +43,12 @@ module.exports = {
restAPI.setup(app);
// listen
server.listen(frappe.config.port);
frappe.app = app;
frappe.server = server;
server.listen(frappe.config.port);
setRouteForPDF();
},
async init() {
@ -52,6 +56,7 @@ module.exports = {
await frappe.init();
frappe.registerModels(frappeModels, 'server');
frappe.registerLibs(common);
await frappe.login();
},

View File

@ -1,6 +1,12 @@
const frappe = require('frappejs');
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const { getTmpDir } = require('frappejs/server/utils');
const { getHTML } = require('frappejs/common/print');
const { getRandomString } = require('frappejs/utils');
module.exports = async function (html, filepath) {
async function makePDF(html, filepath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html);
@ -10,3 +16,29 @@ module.exports = async function (html, filepath) {
});
await browser.close();
}
function setupExpressRoute() {
if (!frappe.app) return;
frappe.app.post('/api/method/pdf', frappe.asyncHandler(handlePDFRequest));
}
async function handlePDFRequest(req, res) {
const args = req.body;
const { doctype, name } = args;
const html = await getHTML(doctype, name);
const filepath = path.join(getTmpDir(), `frappe-pdf-${getRandomString()}.pdf`);
await makePDF(html, filepath);
const file = fs.createReadStream(filepath);
const stat = fs.statSync(filepath);
res.setHeader('Content-Length', stat.size);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename=${path.basename(filepath)}`);
file.pipe(res);
}
module.exports = {
makePDF,
setupExpressRoute
}

View File

@ -1,6 +1,7 @@
const mkdirp = require('mkdirp');
const fs = require('fs');
const getDirName = require('path').dirname;
const os = require('os');
module.exports = {
writeFile(fullpath, contents) {
@ -13,5 +14,9 @@ module.exports = {
});
});
});
},
getTmpDir() {
return os.tmpdir();
}
}

View File

@ -16,7 +16,14 @@ module.exports = {
value = markdown.makeHtml(value || '');
} else if (field.fieldtype === 'Date') {
value = moment(value).format(frappe.SystemSettings.dateFormat.toUpperCase());
let dateFormat;
if (!frappe.SystemSettings) {
dateFormat = 'yyyy-mm-dd';
} else {
dateFormat = frappe.SystemSettings.dateFormat;
}
value = moment(value).format(dateFormat.toUpperCase());
} else {
if (value===null || value===undefined) {

153
yarn.lock
View File

@ -412,6 +412,13 @@ callsites@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
camel-case@3.0.x:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
dependencies:
no-case "^2.2.0"
upper-case "^1.1.1"
camelcase-css@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-1.0.1.tgz#157c4238265f5cf94a1dffde86446552cbf3f705"
@ -511,6 +518,12 @@ clap@^1.0.9:
dependencies:
chalk "^1.1.3"
clean-css@4.1.x:
version "4.1.11"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a"
dependencies:
source-map "0.5.x"
cli-boxes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
@ -613,6 +626,10 @@ commander@2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
commander@2.15.x, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
commander@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
@ -1016,22 +1033,6 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
eslint-config-airbnb-base@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
dependencies:
eslint-restricted-globals "^0.1.1"
eslint-config-airbnb@^16.1.0:
version "16.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz#2546bfb02cc9fe92284bf1723ccf2e87bc45ca46"
dependencies:
eslint-config-airbnb-base "^12.1.0"
eslint-restricted-globals@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
eslint-scope@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
@ -1118,6 +1119,10 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
estree-walker@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e"
estree-walker@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.3.1.tgz#e6b1a51cf7292524e7237c312e5fe6660c1ce1aa"
@ -1369,14 +1374,55 @@ forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
frappe-datatable@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-0.0.3.tgz#55d3fd7bafdf2a7380efab2ae2aaaa956624fca0"
frappe-datatable@frappe/datatable:
version "0.0.4"
resolved "https://codeload.github.com/frappe/datatable/tar.gz/4bb400230087fbf97e8587a34916e14f77fa01cd"
dependencies:
clusterize.js "^0.18.0"
lodash "^4.17.5"
sortablejs "^1.7.0"
frappejs@../frappejs:
version "0.0.5"
dependencies:
autoprefixer "^7.2.4"
awesomplete "^1.1.2"
body-parser "^1.18.2"
bootstrap "^4.0.0"
clusterize.js "^0.18.0"
codemirror "^5.35.0"
commander "^2.13.0"
eslint "^4.19.1"
express "^4.16.2"
flatpickr "^4.3.2"
frappe-datatable frappe/datatable
frappejs "../frappejs"
jquery "^3.3.1"
mkdirp "^0.5.1"
mocha "^4.1.0"
moment "^2.20.1"
mysql "^2.15.0"
node-fetch "^1.7.3"
node-sass "^4.7.2"
nodemon "^1.14.7"
nunjucks "^3.1.0"
octicons "^7.2.0"
popper.js "^1.12.9"
precss "^2.0.0"
puppeteer "^1.2.0"
rollup "^0.55.1"
rollup-plugin-commonjs "^8.3.0"
rollup-plugin-json "^2.3.0"
rollup-plugin-node-resolve "^3.0.2"
rollup-plugin-postcss "^1.2.7"
rollup-plugin-replace "^2.0.0"
rollup-plugin-sass "^0.5.3"
showdown "^1.8.6"
socket.io "^2.0.4"
sortablejs "^1.7.0"
sqlite3 "^3.1.13"
walk "^2.3.9"
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
@ -1672,7 +1718,7 @@ hawk@~6.0.2:
hoek "4.x.x"
sntp "2.x.x"
he@1.1.1:
he@1.1.1, he@1.1.x:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@ -1692,6 +1738,18 @@ html-comment-regex@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
html-minifier@^3.0.2:
version "3.5.14"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.14.tgz#88653b24b344274e3e3d7052f1541ebea054ac60"
dependencies:
camel-case "3.0.x"
clean-css "4.1.x"
commander "2.15.x"
he "1.1.x"
param-case "2.1.x"
relateurl "0.2.x"
uglify-js "3.3.x"
http-errors@1.6.2, http-errors@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
@ -2191,6 +2249,10 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
@ -2381,6 +2443,12 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
dependencies:
lower-case "^1.1.1"
node-fetch@^1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
@ -2652,6 +2720,12 @@ package-json@^4.0.0:
registry-url "^3.0.3"
semver "^5.1.0"
param-case@2.1.x:
version "2.1.1"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
dependencies:
no-case "^2.2.0"
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@ -3444,6 +3518,10 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
relateurl@0.2.x:
version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@ -3601,6 +3679,13 @@ rollup-plugin-commonjs@^8.3.0:
resolve "^1.4.0"
rollup-pluginutils "^2.0.1"
rollup-plugin-html@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-html/-/rollup-plugin-html-0.2.1.tgz#a1862eca87ae54b677689d0d4133911e8226463d"
dependencies:
html-minifier "^3.0.2"
rollup-pluginutils "^1.5.0"
rollup-plugin-json@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-json/-/rollup-plugin-json-2.3.0.tgz#3c07a452c1b5391be28006fbfff3644056ce0add"
@ -3656,6 +3741,13 @@ rollup-plugin-sass@^0.5.3:
estree-walker "^0.3.0"
micromatch "^2.3.11"
rollup-pluginutils@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408"
dependencies:
estree-walker "^0.2.1"
minimatch "^3.0.2"
rollup@^0.55.1:
version "0.55.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.55.1.tgz#baf4f23abe3014b29e56dea7d72d9946e56ac7dd"
@ -3846,17 +3938,17 @@ sortablejs@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.7.0.tgz#80a2b2370abd568e1cec8c271131ef30a904fa28"
source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.3, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.1:
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@ -4161,6 +4253,13 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
uglify-js@3.3.x:
version "3.3.21"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.21.tgz#851a34cbb31840ecb881968ed07dd3a61e7264a0"
dependencies:
commander "~2.15.0"
source-map "~0.6.1"
uid-number@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
@ -4219,6 +4318,10 @@ update-notifier@^2.3.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
upper-case@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
url-parse-lax@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"