From 3d60238ce1674ce5de09f4d292dc411c3960e2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emanuel=20Sch=C3=BCtze?= Date: Wed, 1 Feb 2017 16:53:01 +0100 Subject: [PATCH] Fixed dynamic pdf header/footer and table border (Fixed #2923). Updated pdfmake to 0.1.25. Fixed some pdf style issues. Updated gulp watch task. --- bower.json | 2 +- gulpfile.js | 8 ++- openslides/core/static/js/core/pdf-worker.js | 66 ++++++++++++++++++ openslides/core/static/js/core/pdf.js | 71 ++++++-------------- openslides/motions/static/js/motions/pdf.js | 24 ++----- openslides/utils/validate.py | 2 +- 6 files changed, 103 insertions(+), 70 deletions(-) diff --git a/bower.json b/bower.json index a5f0fda6c..111d30ef4 100644 --- a/bower.json +++ b/bower.json @@ -34,7 +34,7 @@ "ngstorage": "~0.3.11", "ngBootbox": "~0.1.3", "papaparse": "~4.1.2", - "pdfmake": "0.1.23", + "pdfmake": "0.1.25", "roboto-fontface": "~0.6.0" }, "overrides": { diff --git a/gulpfile.js b/gulpfile.js index 62da32e17..69223423e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -228,9 +228,13 @@ gulp.task('default', [ */ // Watches changes in JavaScript and templates. -gulp.task('watch', ['js', 'templates'], function () { - gulp.watch(path.join('openslides', '*', 'static', 'js', '**', '*.js'), ['js']); +gulp.task('watch', ['js', 'templates', 'pdf-worker'], function () { + gulp.watch([ + path.join('openslides', '*', 'static', 'js', '**', '*.js'), + '!' + path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js') + ], ['js']); gulp.watch(path.join('openslides', '*', 'static', 'templates', '**', '*.html'), ['templates']); + gulp.watch(path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js'), ['pdf-worker']); }); // Extracts translatable strings using angular-gettext and saves them in file diff --git a/openslides/core/static/js/core/pdf-worker.js b/openslides/core/static/js/core/pdf-worker.js index 24e453edf..c4c374f54 100644 --- a/openslides/core/static/js/core/pdf-worker.js +++ b/openslides/core/static/js/core/pdf-worker.js @@ -30,6 +30,72 @@ pdfMake.fonts = { // Create PDF on message and return the base64 decoded document self.addEventListener('message', function(e) { var data = JSON.parse(e.data); + + // Workaround for using dynamic footer with page number. + // TODO: Needs improvement of pdfmake's web worker support. + // see https://github.com/bpampuch/pdfmake/issues/38 + if (data.footerTpl) { + data.footer = function (currentPage, pageCount) { + var footerText = data.footerTpl.text + .replace('{{currentPage}}', currentPage) + .replace('{{pageCount}}', pageCount); + return { + text: footerText, + alignment: data.footerTpl.alignment, + fontSize: data.footerTpl.fontSize, + color: data.footerTpl.color + }; + }; + } + + // Workaround for using table layout functions. + // TODO: Needs improvement of pdfmake's web worker support. + // Currently only functions are allowed for 'layout'. + // But functions cannot be passed to workers (via JSON). + // + // ballot paper crop marks + for (var i = 0; i < data.content.length; i++) { + if (data.content[i].layout === "{{ballot-placeholder-to-insert-functions-here}}") { + data.content[i].layout = { + hLineWidth: function(i, node) { + if (i === 0){ + return 0; + } else if (i === node.table.body.length) { + if (node.rowsperpage && node.rowsperpage > i) { + return 0.5; + } else { + return 0; + } + } else { + return 0.5; + } + }, + vLineWidth: function(i, node) { + return (i === 0 || i === node.table.widths.length) ? 0 : 0.5; + }, + hLineColor: function() { + return 'gray'; + }, + vLineColor: function() { + return 'gray'; + } + }; + } + // motion meta table border lines + if (data.content[i].layout === "{{motion-placeholder-to-insert-functions-here}}") { + data.content[i].layout = { + hLineWidth: function(i, node) { + return (i === 0 || i === node.table.body.length) ? 0 : 0.5; + }, + vLineWidth: function() { + return 0; + }, + hLineColor: function() { + return 'white'; + } + }; + } + } var pdf = pdfMake.createPdf(data); pdf.getBase64(function (base64) { self.postMessage(base64); diff --git a/openslides/core/static/js/core/pdf.js b/openslides/core/static/js/core/pdf.js index addd27aca..9c49513e3 100644 --- a/openslides/core/static/js/core/pdf.js +++ b/openslides/core/static/js/core/pdf.js @@ -84,40 +84,7 @@ angular.module('OpenSlidesApp.core.pdf', []) // crop marks for ballot papers PDFLayout.getBallotLayoutLines = function() { - return { - hLineWidth: function(i, node) { - if (i === 0){ - return 0; - } else if (i === node.table.body.length) { - if (node.rowsperpage && node.rowsperpage > i) { - return 0.5; - } else { - return 0; - } - } else { - return 0.5; - } - }, - vLineWidth: function(i, node) { - return (i === 0 || i === node.table.widths.length) ? 0 : 0.5; - }, - hLineColor: function(i, node) { - if (i === 0){ - return 'none'; - } else if (i === node.table.body.length) { - if (node.rowsperpage && node.rowsperpage > i) { - return 'gray'; - } else { - return 'none'; - } - } else { - return 'gray'; - } - }, - vLineColor: function(i, node) { - return (i === 0 || i === node.table.widths.length) ? 'none' : 'gray'; - }, - }; + return '{{ballot-placeholder-to-insert-functions-here}}'; }; return PDFLayout; @@ -161,8 +128,7 @@ angular.module('OpenSlidesApp.core.pdf', []) */ var createInstance = function(contentProvider) { // PDF header - var header = function() { - var date = new Date(); + var getHeader = function() { var columns = []; // add here your custom logo (which has to be added to a custom vfs_fonts.js) @@ -196,14 +162,17 @@ angular.module('OpenSlidesApp.core.pdf', []) }; }; + // PDF footer - var footer = function(currentPage, pageCount) { + // Used placeholder for currentPage and pageCount which + // are replaced by dynamic footer function in pdf-worker.js. + var getFooter = function() { return { alignment: 'center', fontSize: 8, color: '#555', - text: gettextCatalog.getString('Page') + ' ' + currentPage.toString() + - ' / ' + pageCount.toString() + text: gettextCatalog.getString('Page') + + ' {{currentPage}} / {{pageCount}}' }; }; // Generates the document(definition) for pdfMake @@ -211,13 +180,13 @@ angular.module('OpenSlidesApp.core.pdf', []) var content = contentProvider.getContent(); return { pageSize: 'A4', - pageMargins: [80, 90, 80, 60], + pageMargins: [80, 90, 80, 100], defaultStyle: { font: 'PdfFont', fontSize: 10 }, - header: header, - footer: noFooter ? '' : footer, + header: getHeader(), + footerTpl: noFooter ? '' : getFooter(), content: content, styles: { title: { @@ -393,12 +362,12 @@ angular.module('OpenSlidesApp.core.pdf', []) "u": ["text-decoration:underline"], "em": ["font-style:italic"], "i": ["font-style:italic"], - "h1": ["font-size:30"], - "h2": ["font-size:28"], - "h3": ["font-size:26"], - "h4": ["font-size:24"], - "h5": ["font-size:22"], - "h6": ["font-size:20"], + "h1": ["font-size:14", "font-weight:bold"], + "h2": ["font-size:12", "font-weight:bold"], + "h3": ["font-size:10", "font-weight:bold"], + "h4": ["font-size:10", "font-style:italic"], + "h5": ["font-size:10"], + "h6": ["font-size:10"], "a": ["color:blue", "text-decoration:underline"], "del": ["color:red", "text-decoration:line-through"], "ins": ["color:green", "text-decoration:underline"] @@ -563,6 +532,8 @@ angular.module('OpenSlidesApp.core.pdf', []) case "h5": case "h6": currentParagraph = create("text"); + currentParagraph.marginBottom = 4; + currentParagraph.marginTop = 10; /* falls through */ case "a": currentParagraph = parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]), diff_mode); @@ -666,6 +637,7 @@ angular.module('OpenSlidesApp.core.pdf', []) //in case of inline-line-numbers and the os-line-break class ignore the break if (!(lineNumberMode == "inline" && element.getAttribute("class") == "os-line-break")) { currentParagraph = create("text"); + currentParagraph.lineHeight = 1.25; alreadyConverted.push(currentParagraph); } break; @@ -680,7 +652,8 @@ angular.module('OpenSlidesApp.core.pdf', []) break; case "p": currentParagraph = create("text"); - currentParagraph.margin = [0,5]; + currentParagraph.marginTop = 8; + currentParagraph.lineHeight = 1.25; var stackP = create("stack"); stackP.stack.push(currentParagraph); ComputeStyle(stackP, styles); diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index 75f2847bb..47a020348 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -40,7 +40,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) metaTableBody.push([ { text: gettextCatalog.getString('Submitters') + ':', - style: ['bold', 'grey'] + style: ['bold', 'grey'], }, { text: submitters, @@ -79,8 +79,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) metaTableBody.push([ { text: gettextCatalog.getString('Category') + ':', - style: ['bold', 'grey'] - }, + style: ['bold', 'grey'] }, { text: motion.category.name, style: 'grey' @@ -216,26 +215,17 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) } // build table + // Used placeholder for 'layout' functions whiche are + // replaced by lineWitdh/lineColor function in pfd-worker.js. + // TODO: Remove placeholder and us static values for LineWidth and LineColor + // if pdfmake has fixed this. var metaTableJsonString = { table: { widths: ['30%','70%'], body: metaTableBody, }, margin: [0, 0, 0, 20], - layout: { - hLineWidth: function(i, node) { - return (i === 0 || i === node.table.body.length) ? 0 : 0.5; - }, - vLineWidth: function(i, node) { - return (i === 0 || i === node.table.widths.length) ? 0 : 0; - }, - hLineColor: function(i, node) { - return (i === 0 || i === node.table.body.length) ? '' : 'white'; - }, - vLineColor: function(i, node) { - return (i === 0 || i === node.table.widths.length) ? '' : 'white'; - } - } + layout: '{{motion-placeholder-to-insert-functions-here}}' }; return metaTableJsonString; }; diff --git a/openslides/utils/validate.py b/openslides/utils/validate.py index a317ab5aa..acda0dedd 100644 --- a/openslides/utils/validate.py +++ b/openslides/utils/validate.py @@ -2,7 +2,7 @@ import bleach allowed_tags = [ 'a', 'img', # links and images - 'p', 'span', 'blockquote', # text layout + 'br', 'p', 'span', 'blockquote', # text layout 'strike', 'strong', 'u', 'em', 'sup', 'sub', 'pre', # text formatting 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', # headings 'ol', 'ul', 'li', # lists