diff --git a/app/.jshintrc b/app/.jshintrc new file mode 100644 index 00000000..88874e4d --- /dev/null +++ b/app/.jshintrc @@ -0,0 +1,9 @@ +{ + "curly": true, + "node": true, + "indent": 4, + "latedef": true, + "undef": true, + "unused": true, + "expr": true +} diff --git a/app/index.js b/app/index.js new file mode 100644 index 00000000..a5c70570 --- /dev/null +++ b/app/index.js @@ -0,0 +1,38 @@ +var express = require('express'); +var app = express(); + +// Configure ejs engine +app.set('views', __dirname + '/../public'); +app.engine('html', require('ejs').renderFile); + +// Force HTTPS on stackedit.io +app.all('*', function(req, res, next) { + if (req.headers.host == 'stackedit.io' && req.headers['x-forwarded-proto'] != 'https') { + res.redirect('https://stackedit.io' + req.url); + } + else { + /\.(eot|ttf|woff)$/.test(req.url) && res.header('Access-Control-Allow-Origin', '*'); + next(); + } +}); + +// Use gzip compression +app.use(express.compress()); + +// Serve static resources +app.use(express.static(__dirname + '/../public')); + +// Serve viewer.html in /viewer +app.get('/viewer', function (req, res) { + res.render('viewer.html'); +}); + +app.post('/pdf', require('./pdf')); + +// Error 404 +app.use(function(req, res, next) { + res.status(404); + res.render('error_404.html'); +}); + +module.exports = app; diff --git a/app/pdf.js b/app/pdf.js new file mode 100644 index 00000000..cc72c864 --- /dev/null +++ b/app/pdf.js @@ -0,0 +1,75 @@ +var spawn = require('child_process').spawn; +var fs = require('fs'); + +var authorizedPageSizes = [ + 'A3', + 'A4', + 'Legal', + 'Letter' +]; + +module.exports = function(req, res, next) { + var options, params = []; + try { + options = JSON.parse(req.query.options); + } + catch(e) { + options = {}; + } + + // Margins + var marginRight = parseInt(options.marginRight); + params.push('-R', isNaN(marginRight) ? 25 : marginRight); + var marginTop = parseInt(options.marginTop); + params.push('-T', isNaN(marginTop) ? 25 : marginTop); + var marginBottom = parseInt(options.marginBottom); + params.push('-B', isNaN(marginBottom) ? 25 : marginBottom); + var marginLeft = parseInt(options.marginLeft); + params.push('-L', isNaN(marginLeft) ? 25 : marginLeft); + + // Header + options.headerCenter && params.push('--header-center', options.headerCenter); + options.headerLeft && params.push('--header-left', options.headerLeft); + options.headerRight && params.push('--header-left', options.headerRight); + options.headerFontName && params.push('--header-font-name ', options.headerFontName); + options.headerFontSize && params.push('--header-font-size ', options.headerFontSize); + + // Footer + options.footerCenter && params.push('--footer-center', options.footerCenter); + options.footerLeft && params.push('--footer-left', options.footerLeft); + options.footerRight && params.push('--footer-left', options.footerRight); + options.footerFontName && params.push('--footer-font-name ', options.footerFontName); + options.footerFontSize && params.push('--footer-font-size ', options.footerFontSize); + + // Page size + var pageSize = options.pageSize; + params.push('--page-size', authorizedPageSizes.indexOf(pageSize) === -1 ? 'A4' : pageSize); + + // wkhtmltopdf can't access /dev/stdout on Amazon EC2 for some reason + var filePath = '/tmp/' + Date.now() + '.pdf'; + var wkhtmltopdf = spawn('wkhtmltopdf', params.concat('--javascript-delay', '6000', '-', filePath), { + stdio: [ + 'pipe', + 'ignore', + 'ignore' + ] + }); + function onError(err) { + next(err); + } + wkhtmltopdf.on('error', onError); + wkhtmltopdf.stdin.on('error', onError); + wkhtmltopdf.on('close', function(code) { + if(code) { + res.statusCode = 400; + return res.end('Unknown error'); + } + var readStream = fs.createReadStream(filePath); + readStream.on('close', function() { + fs.unlink(filePath, function() { + }); + }); + readStream.pipe(res); + }); + req.pipe(wkhtmltopdf.stdin); +}; diff --git a/public/res/constants.js b/public/res/constants.js index e081e790..7495927e 100644 --- a/public/res/constants.js +++ b/public/res/constants.js @@ -26,7 +26,7 @@ define([], function() { constants.DOWNLOAD_PROXY_URL = "https://stackedit-download-proxy.herokuapp.com/"; constants.PICASA_PROXY_URL = "https://stackedit-picasa-proxy.herokuapp.com/"; constants.SSH_PROXY_URL = "https://stackedit-ssh-proxy.herokuapp.com/"; - constants.HTMLTOPDF_URL = "https://stackedit-htmltopdf.herokuapp.com/"; + constants.HTMLTOPDF_URL = "/pdf"; // Site dependent constants.BASE_URL = "http://localhost/"; diff --git a/public/res/publisher.js b/public/res/publisher.js index 82ef37e1..f2b0b53a 100644 --- a/public/res/publisher.js +++ b/public/res/publisher.js @@ -348,7 +348,6 @@ define([ var xhr = new XMLHttpRequest(); xhr.open('POST', constants.HTMLTOPDF_URL, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhr.setRequestHeader('page-size', settings.pdfPageSize); xhr.responseType = 'blob'; xhr.onreadystatechange = function() { if(this.readyState == 4) { diff --git a/public/res/styles/base.less b/public/res/styles/base.less index 8f59053b..db817a79 100644 --- a/public/res/styles/base.less +++ b/public/res/styles/base.less @@ -218,6 +218,10 @@ hr { margin: 2em 0; } +img { + max-width: 100%; +} + .sequence-diagram, .flow-chart { text-align: center; margin-bottom: @p-margin; @@ -468,14 +472,3 @@ body.rtl { direction: rtl; } } - -/******************* - * PDF - *******************/ -body.pdf { - font-family: "DejaVu Sans"; - - code, pre { - font-family: "DejaVu Sans Mono"; - } -} diff --git a/public/res/styles/main.less b/public/res/styles/main.less index 81b44f7e..cbe1602b 100644 --- a/public/res/styles/main.less +++ b/public/res/styles/main.less @@ -1558,9 +1558,10 @@ div.jGrowl { -ms-filter: none; filter: none; border-radius: 4px; + font-family: @font-family-base; } div.jGrowl-notification { - min-height: 60px; + min-height: 65px; } } diff --git a/server.js b/server.js index a405c0c9..12e8bbe2 100644 --- a/server.js +++ b/server.js @@ -1,40 +1,7 @@ -var express = require('express'); -var app = express(); - -// Configure ejs engine -app.set('views', __dirname + '/public'); -app.engine('html', require('ejs').renderFile); - -// Force HTTPS on stackedit.io -app.all('*', function(req, res, next) { - if (req.headers.host == 'stackedit.io' && req.headers['x-forwarded-proto'] != 'https') { - res.redirect('https://stackedit.io' + req.url); - } - else { - /\.(eot|ttf|woff)$/.test(req.url) && res.header('Access-Control-Allow-Origin', '*'); - next(); - } -}); - -// Use gzip compression -app.use(express.compress()); - -// Serve static resources -app.use(express.static(__dirname + '/public')); - -// Serve viewer.html in /viewer -app.get('/viewer', function (req, res) { - res.render('viewer.html'); -}); - -// Error 404 -app.use(function(req, res, next) { - res.status(404); - res.render('error_404.html'); -}); +var app = require('./app'); // Listen on port 3000 var port = process.env.PORT || 3000; app.listen(port, null, function() { - console.log('Server started: http://localhost:' + port); + console.log('Server started: http://localhost:' + port); });