From 2cb1429c8da78c0b451536c1fa227fcd0d53950b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Ho=CC=88=C3=9Fl?= Date: Sat, 3 Dec 2016 15:39:57 +0100 Subject: [PATCH] First attempt to mitigate the list / line number problem --- openslides/core/static/js/core/pdf.js | 71 +++++++-- openslides/motions/static/js/motions/pdf.js | 6 +- tests/karma/core/pdf.service.test.js | 163 ++++++++++++++++++++ tests/karma/karma.conf.js | 1 + 4 files changed, 227 insertions(+), 14 deletions(-) create mode 100644 tests/karma/core/pdf.service.test.js diff --git a/openslides/core/static/js/core/pdf.js b/openslides/core/static/js/core/pdf.js index b5a8c4ec4..563bf9299 100644 --- a/openslides/core/static/js/core/pdf.js +++ b/openslides/core/static/js/core/pdf.js @@ -380,8 +380,9 @@ angular.module('OpenSlidesApp.core.pdf', []) * Convertes HTML for use with pdfMake * @function * @param {object} html - html + * @param {string} lineNumberMode - [inline, outside, none] */ - convertHTML = function(html, scope) { + convertHTML = function(html, lineNumberMode) { var elementStyles = { "b": ["font-weight:bold"], "strong": ["font-weight:bold"], @@ -402,6 +403,31 @@ angular.module('OpenSlidesApp.core.pdf', []) "delete": ["color:red", "text-decoration:line-through"], "insert": ["color:green", "text-decoration:underline"] }, + /** + * Removes all line number nodes (not line-breaks) + * and returns an array containing the reoved numbers (as integer, not as node) + * + * @function + * @param {object} element + */ + extractLineNumbers = function(element) { + var foundLineNumbers = []; + if (element.nodeName == 'SPAN' && element.getAttribute('class').indexOf('os-line-number') > -1) { + foundLineNumbers.push(element.getAttribute('data-line-number')); + element.parentNode.removeChild(element); + } else { + var children = element.childNodes, + childrenLength = children.length; + for (var i = 0; i < children.length; i++) { + foundLineNumbers = _.union(foundLineNumbers, extractLineNumbers(children[i])); + if (children.length < childrenLength) { + i -= (childrenLength - children.length); + childrenLength = children.length; + } + } + } + return foundLineNumbers; + }, /** * Parses Children of the current paragraph * @function @@ -593,7 +619,7 @@ angular.module('OpenSlidesApp.core.pdf', []) alreadyConverted.push(st); break; case "span": - if (scope.lineNumberMode == "inline") { + if (lineNumberMode == "inline") { if (diff_mode != DIFF_MODE_INSERT) { var lineNumberInline = element.getAttribute("data-line-number"), lineNumberObjInline = { @@ -604,7 +630,7 @@ angular.module('OpenSlidesApp.core.pdf', []) currentParagraph.text.push(lineNumberObjInline); } parseChildren(alreadyConverted, element, currentParagraph, styles, diff_mode); - } else if (scope.lineNumberMode == "outside") { + } else if (lineNumberMode == "outside") { var lineNumberOutline; if (diff_mode == DIFF_MODE_INSERT) { lineNumberOutline = ""; @@ -633,7 +659,7 @@ angular.module('OpenSlidesApp.core.pdf', []) break; case "br": //in case of inline-line-numbers and the os-line-break class ignore the break - if (!(scope.lineNumberMode == "inline" && element.getAttribute("class") == "os-line-break")) { + if (!(lineNumberMode == "inline" && element.getAttribute("class") == "os-line-break")) { currentParagraph = create("text"); alreadyConverted.push(currentParagraph); } @@ -686,14 +712,37 @@ angular.module('OpenSlidesApp.core.pdf', []) }); break; case "ul": - var u = create("ul"); - parseChildren(u.ul, element, currentParagraph, styles, diff_mode); - alreadyConverted.push(u); - break; case "ol": - var o = create("ol"); - parseChildren(o.ol, element, currentParagraph, styles, diff_mode); - alreadyConverted.push(o); + var list = create(nodeName); + if (lineNumberMode == "outside") { + var lines = extractLineNumbers(element); + parseChildren(list[nodeName], element, currentParagraph, styles, diff_mode); + if (lines.length > 0) { + var listCol = { + columns: [{ + width: 20, + stack: [] + }] + }; + _.forEach(lines, function(line) { + listCol.columns[0].stack.push({ + width: 20, + text: line, + color: "gray", + fontSize: 8, + margin: [0, 2.35, 0, 0] + }); + }); + listCol.columns.push(list); + listCol.margin = [0,10,0,0]; + alreadyConverted.push(listCol); + } else { + alreadyConverted.push(list); + } + } else { + parseChildren(list[nodeName], element, currentParagraph, styles, diff_mode); + alreadyConverted.push(list); + } break; default: var temporary = create("text", element.textContent.replace(/\n/g, "")); diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index 1be009425..deca7e3e9 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -257,9 +257,9 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) * https://github.com/OpenSlides/OpenSlides/issues/2361 */ var text = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version); - return converter.convertHTML(text, $scope); + return converter.convertHTML(text, $scope.lineNumberMode); } else { - return converter.convertHTML(motion.getText($scope.version), $scope); + return converter.convertHTML(motion.getText($scope.version), $scope.lineNumberMode); } }; @@ -271,7 +271,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) text: gettextCatalog.getString('Reason'), style: 'heading3' }); - reason.push(converter.convertHTML(motion.getReason($scope.version), $scope)); + reason.push(converter.convertHTML(motion.getReason($scope.version), $scope.lineNumberMode)); } return reason; }; diff --git a/tests/karma/core/pdf.service.test.js b/tests/karma/core/pdf.service.test.js new file mode 100644 index 000000000..2b4807257 --- /dev/null +++ b/tests/karma/core/pdf.service.test.js @@ -0,0 +1,163 @@ +describe('pdf', function () { + + beforeEach(module('OpenSlidesApp.core.pdf')); + beforeEach(module('OpenSlidesApp.motions.lineNumbering')); + + var PdfMakeConverter, + lineNumberingService; + + beforeEach(inject(function (_PdfMakeConverter_, _lineNumberingService_) { + PdfMakeConverter = _PdfMakeConverter_; + lineNumberingService = _lineNumberingService_; + })); + + var defaultmargin = [0, 5], + emptyline = JSON.stringify({ + "stack": [ + {"text": [], "margin": defaultmargin}] + + }); + + + describe('converting html to pdfmake', function () { + it('converts a simple html string', function () { + var instance = PdfMakeConverter.createInstance(); + var pdfmake = instance.convertHTML( + '

Hello strong world

', 'none' + ); + expect(JSON.stringify(pdfmake[0])).toBe(emptyline); + expect(JSON.stringify(pdfmake[1])).toBe(JSON.stringify({ + "stack": [ + { + "text": [ + {"text": "Hello "}, + {"text": "strong", "bold": true}, + {"text": " world"} + ], "margin": defaultmargin + } + ] + })); + expect(JSON.stringify(pdfmake[2])).toBe(emptyline); + }); + + + it('converts a simple list', function () { + var instance = PdfMakeConverter.createInstance(); + var pdfmake = instance.convertHTML( + '', 'none' + ); + expect(JSON.stringify(pdfmake[0])).toBe(emptyline); + expect(JSON.stringify(pdfmake[1])).toBe(JSON.stringify({ + "ul": [ + {"stack": [{"text": [{"text": "Point 1"}]}]}, + {"stack": [{"text": [{"text": "Point 2"}]}]} + ] + })); + expect(JSON.stringify(pdfmake[2])).toBe(emptyline); + }); + + + it('converts simple text including line numbers (inline)', function () { + var inHtml = "Test", + numberedHtml = lineNumberingService.insertLineNumbers(inHtml, 5); + var instance = PdfMakeConverter.createInstance(); + var pdfmake = instance.convertHTML(numberedHtml, 'inline'); + expect(JSON.stringify(pdfmake)).toBe(JSON.stringify([ + { + "stack": [{ + "text": [ + {"text": "1", "color": "gray", "fontSize": 5}, + {"text": " "}, + { + "text": null, + "color": "gray", + "fontSize": 5 + }, + {"text": "Test"} + ], "margin": [0, 5] + }] + } + ])); + }); + + + it('converts simple text including line numbers (outside)', function () { + var inHtml = "

Test Test2 Test3

", + numberedHtml = lineNumberingService.insertLineNumbers(inHtml, 5); + var instance = PdfMakeConverter.createInstance(); + var pdfmake = instance.convertHTML(numberedHtml, 'outside'); + + expect(JSON.stringify(pdfmake[0])).toBe(emptyline); + expect(JSON.stringify(pdfmake[1])).toBe(JSON.stringify({ + "stack": [{"text": [], "margin": [0, 5]}, { + "columns": [{ + "width": 20, + "text": "1", + "color": "gray", + "fontSize": 8, + "margin": [0, 2, 0, 0] + }, {"text": [{"text": " "}, {"text": "Test "}]}] + }, {"text": []}, { + "columns": [{ + "width": 20, + "text": "2", + "color": "gray", + "fontSize": 8, + "margin": [0, 2, 0, 0] + }, {"text": [{"text": " "}, {"text": "Test2 "}]}] + }, {"text": []}, { + "columns": [{ + "width": 20, + "text": "3", + "color": "gray", + "fontSize": 8, + "margin": [0, 2, 0, 0] + }, {"text": [{"text": " "}, {"text": "Test3"}]}] + }] + })); + expect(JSON.stringify(pdfmake[2])).toBe(emptyline); + }); + + it('converts a list including line numbers (outside)', function () { + var inHtml = "", + numberedHtml = lineNumberingService.insertLineNumbers(inHtml, 10); + + var instance = PdfMakeConverter.createInstance(); + var pdfmake = instance.convertHTML(numberedHtml, 'outside'); + + expect(JSON.stringify(pdfmake[0])).toBe(emptyline); + expect(JSON.stringify(pdfmake[1])).toBe(JSON.stringify({ + "columns": [ + { + "width": 20, + "stack": [ + {"width": 20, "text": "1", "color": "gray", "fontSize": 8, "margin": [0, 2.35, 0, 0]}, + {"width": 20, "text": "2", "color": "gray", "fontSize": 8, "margin": [0, 2.35, 0, 0]}, + {"width": 20, "text": "3", "color": "gray", "fontSize": 8, "margin": [0, 2.35, 0, 0]}, + {"width": 20, "text": "4", "color": "gray", "fontSize": 8, "margin": [0, 2.35, 0, 0]} + ] + }, + { + "ul": [{ + "stack": [ + {"text": [{"text": "Item "}]}, + {"text": [{"text": "1"}]} + ] + }, { + "stack": [ + {"text": [{"text": "Item "}]}, + {"text": [{"text": "2"}]} + ] + } + ] + } + ], + "margin": [0,10,0,0] + })); + expect(JSON.stringify(pdfmake[2])).toBe(emptyline); + }) + }); +}); \ No newline at end of file diff --git a/tests/karma/karma.conf.js b/tests/karma/karma.conf.js index b3e7e9554..d2d146d36 100644 --- a/tests/karma/karma.conf.js +++ b/tests/karma/karma.conf.js @@ -19,6 +19,7 @@ module.exports = function(config) { 'node_modules/angular-mocks/angular-mocks.js', 'openslides/motions/static/js/motions/linenumbering.js', 'openslides/motions/static/js/motions/diff.js', + 'openslides/core/static/js/core/pdf.js', 'tests/karma/*/*.test.js' ],