diff --git a/CHANGELOG b/CHANGELOG index 8b94e1b2f..565331646 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ Motions: - Removed server side image to base64 transformation and added local transformation [#3181] - Added support for export motions in a zip archive [#3189]. +- Performance improvement for zip creation [#3251] - Bugfix: changing motion line length did not invalidate cache [#3202] - Bugfix: Added more distance in motion PDF for DEL-tags in new lines [#3211]. - Added warning message if an edit dialog was already opened by another @@ -48,7 +49,7 @@ Core: General: - Switched from npm to Yarn [#3188]. - Several bugfixes and minor improvements. -- Bugfixes for PDF creation [#3227] +- Bugfixes for PDF creation [#3227, #3251] Version 2.1.1 (2017-04-05) diff --git a/openslides/core/static/js/core/pdf-worker.js b/openslides/core/static/js/core/pdf-worker.js index 4300bb558..4d4d4b303 100644 --- a/openslides/core/static/js/core/pdf-worker.js +++ b/openslides/core/static/js/core/pdf-worker.js @@ -86,34 +86,46 @@ var replacePlaceholder = function (content) { } }; - -// 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) { - for(var i = 0; i < data.footerTpl.columns.length; i++) { - if (data.footerTpl.columns[i].text) { - data.footerTpl.columns[i].text = data.footerTpl.columns[i].text +// 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 +var replaceFooter = function (doc) { + if (doc.footerTpl) { + doc.footer = function (currentPage, pageCount) { + // One way to clone arrays/objects in js, that are serilizeable. + var columns = JSON.parse(JSON.stringify(doc.footerTpl.columns)); + for (var i = 0; i < columns.length; i++) { + if (columns[i].text) { + columns[i].text = columns[i].text .replace('{{currentPage}}', currentPage) .replace('{{pageCount}}', pageCount); } } return { - columns: data.footerTpl.columns, - margin: data.footerTpl.margin, + columns: columns, + margin: doc.footerTpl.margin, }; }; } +}; - replacePlaceholder(data.content); +// Create PDF on message and return the base64 decoded document +self.addEventListener('message', function(e) { + var data = JSON.parse(e.data); + var doc = data.pdfDocument; - var pdf = pdfMake.createPdf(data); + replaceFooter(doc); + replacePlaceholder(doc.content); + + var pdf = pdfMake.createPdf(doc); pdf.getBase64(function (base64) { - self.postMessage(base64); + if (data.filename) { + self.postMessage(JSON.stringify({ + filename: data.filename, + base64: base64 + })); + } else { + self.postMessage(base64); + } }); }, false); diff --git a/openslides/core/static/js/core/pdf.js b/openslides/core/static/js/core/pdf.js index c9f140f3c..9c4b693c0 100644 --- a/openslides/core/static/js/core/pdf.js +++ b/openslides/core/static/js/core/pdf.js @@ -1008,7 +1008,34 @@ angular.module('OpenSlidesApp.core.pdf', []) pdfWorker.addEventListener('error', function (event) { reject(event); }); - pdfWorker.postMessage(JSON.stringify(pdfDocument)); + pdfWorker.postMessage(JSON.stringify({ + pdfDocument: pdfDocument + })); + }); + }, + // Struckture of pdfDocuments: { filname1: doc, filename2: doc, ...} + getBase64FromMultipleDocuments: function (pdfDocuments) { + return $q(function (resolve, reject) { + var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); + var resultCount = 0; + var base64Map = {}; // Maps filename to base64 + pdfWorker.addEventListener('message', function (event) { + resultCount++; + var data = JSON.parse(event.data); + base64Map[data.filename] = data.base64; + if (resultCount === _.keys(pdfDocuments).length) { + resolve(base64Map); + } + }); + pdfWorker.addEventListener('error', function (event) { + reject(event); + }); + _.forEach(pdfDocuments, function (doc, filename) { + pdfWorker.postMessage(JSON.stringify({ + filename: filename, + pdfDocument: doc + })); + }); }); }, download: function (pdfDocument, filename) { diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index ae7f61312..e8ee416ce 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -723,9 +723,9 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) params.filename = void 0; // clear this, so we do not override the default filenames for each pdf. var self = this; - var pdfs = {}; var usedFilenames = []; - var pdfPromises = _.map(motions, function (motion) { + var docMap = {}; + var docPromises = _.map(motions, function (motion) { var identifier = motion.identifier ? '-' + motion.identifier : ''; var filename = gettextCatalog.getString('Motion') + identifier; @@ -742,36 +742,30 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) usedFilenames.push(filename); filename += '.pdf'; - return $q(function (resolve, reject) { + return $q(function (resolve) { // get documentProvider for every motion. self.getDocumentProvider(motion, params, true).then(function (documentProvider) { - var doc = documentProvider.getDocument(); - - PdfCreate.getBase64FromDocument(doc).then(function (data) { - pdfs[filename] = data; - resolve(); - }, function (error) { - reject(error); - }); + docMap[filename] = documentProvider.getDocument(); + resolve(); }); }); }); - - // Wait for all documents to be generated. Then put them into a zip and download it. - $q.all(pdfPromises).then(function () { - var zip = new JSZip(); - _.forEach(pdfs, function (data, filename) { - zip.file(filename, data, {base64: true}); + $q.all(docPromises).then(function () { + PdfCreate.getBase64FromMultipleDocuments(docMap).then(function (pdfMap) { + var zip = new JSZip(); + _.forEach(pdfMap, function (data, filename) { + zip.file(filename, data, {base64: true}); + }); + Messaging.createOrEditMessage(messageId, '' + + gettextCatalog.getString('ZIP successfully generated.'), 'success', {timeout: 3000}); + zip.generateAsync({type: 'blob'}).then(function (content) { + FileSaver.saveAs(content, zipFilename); + }); + }, function (error) { + Messaging.createOrEditMessage(messageId, '' + gettextCatalog.getString('Error while generating ZIP file') + + ': ' + error + '', 'error'); }); - Messaging.createOrEditMessage(messageId, '' + - gettextCatalog.getString('ZIP successfully generated.'), 'success', {timeout: 3000}); - zip.generateAsync({type: 'blob'}).then(function (content) { - FileSaver.saveAs(content, zipFilename); - }); - }, function (error) { - Messaging.createOrEditMessage(messageId, '' + gettextCatalog.getString('Error while generating ZIP file') + - ': ' + error + '', 'error'); }); }, createPollPdf: function (motion, version) {