Merge pull request #2739 from CatoTH/Issue2561-pdf-line-numbering-in-lists

Attempt to mitigate the list / line number problem
This commit is contained in:
Norman Jäckel 2016-12-07 11:34:50 +01:00 committed by GitHub
commit 4e5adb74e3
4 changed files with 227 additions and 14 deletions

View File

@ -380,8 +380,9 @@ angular.module('OpenSlidesApp.core.pdf', [])
* Convertes HTML for use with pdfMake * Convertes HTML for use with pdfMake
* @function * @function
* @param {object} html - html * @param {object} html - html
* @param {string} lineNumberMode - [inline, outside, none]
*/ */
convertHTML = function(html, scope) { convertHTML = function(html, lineNumberMode) {
var elementStyles = { var elementStyles = {
"b": ["font-weight:bold"], "b": ["font-weight:bold"],
"strong": ["font-weight:bold"], "strong": ["font-weight:bold"],
@ -402,6 +403,31 @@ angular.module('OpenSlidesApp.core.pdf', [])
"delete": ["color:red", "text-decoration:line-through"], "delete": ["color:red", "text-decoration:line-through"],
"insert": ["color:green", "text-decoration:underline"] "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 * Parses Children of the current paragraph
* @function * @function
@ -593,7 +619,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
alreadyConverted.push(st); alreadyConverted.push(st);
break; break;
case "span": case "span":
if (scope.lineNumberMode == "inline") { if (lineNumberMode == "inline") {
if (diff_mode != DIFF_MODE_INSERT) { if (diff_mode != DIFF_MODE_INSERT) {
var lineNumberInline = element.getAttribute("data-line-number"), var lineNumberInline = element.getAttribute("data-line-number"),
lineNumberObjInline = { lineNumberObjInline = {
@ -604,7 +630,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
currentParagraph.text.push(lineNumberObjInline); currentParagraph.text.push(lineNumberObjInline);
} }
parseChildren(alreadyConverted, element, currentParagraph, styles, diff_mode); parseChildren(alreadyConverted, element, currentParagraph, styles, diff_mode);
} else if (scope.lineNumberMode == "outside") { } else if (lineNumberMode == "outside") {
var lineNumberOutline; var lineNumberOutline;
if (diff_mode == DIFF_MODE_INSERT) { if (diff_mode == DIFF_MODE_INSERT) {
lineNumberOutline = ""; lineNumberOutline = "";
@ -633,7 +659,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
break; break;
case "br": case "br":
//in case of inline-line-numbers and the os-line-break class ignore the break //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"); currentParagraph = create("text");
alreadyConverted.push(currentParagraph); alreadyConverted.push(currentParagraph);
} }
@ -686,14 +712,37 @@ angular.module('OpenSlidesApp.core.pdf', [])
}); });
break; break;
case "ul": case "ul":
var u = create("ul");
parseChildren(u.ul, element, currentParagraph, styles, diff_mode);
alreadyConverted.push(u);
break;
case "ol": case "ol":
var o = create("ol"); var list = create(nodeName);
parseChildren(o.ol, element, currentParagraph, styles, diff_mode); if (lineNumberMode == "outside") {
alreadyConverted.push(o); 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; break;
default: default:
var temporary = create("text", element.textContent.replace(/\n/g, "")); var temporary = create("text", element.textContent.replace(/\n/g, ""));

View File

@ -257,9 +257,9 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
* https://github.com/OpenSlides/OpenSlides/issues/2361 * https://github.com/OpenSlides/OpenSlides/issues/2361
*/ */
var text = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version); var text = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version);
return converter.convertHTML(text, $scope); return converter.convertHTML(text, $scope.lineNumberMode);
} else { } 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'), text: gettextCatalog.getString('Reason'),
style: 'heading3' style: 'heading3'
}); });
reason.push(converter.convertHTML(motion.getReason($scope.version), $scope)); reason.push(converter.convertHTML(motion.getReason($scope.version), $scope.lineNumberMode));
} }
return reason; return reason;
}; };

View File

@ -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(
'<p>Hello <strong>strong</strong> world</p>', '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(
'<ul>' +
'<li>Point 1</li>' +
'<li>Point 2</li>' +
'</ul>', '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 = "<span>Test</span>",
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 = "<p>Test Test2 Test3</p>",
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 = "<ul><li>Item 1</li><li>Item 2</li></ul>",
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);
})
});
});

View File

@ -19,6 +19,7 @@ module.exports = function(config) {
'node_modules/angular-mocks/angular-mocks.js', 'node_modules/angular-mocks/angular-mocks.js',
'openslides/motions/static/js/motions/linenumbering.js', 'openslides/motions/static/js/motions/linenumbering.js',
'openslides/motions/static/js/motions/diff.js', 'openslides/motions/static/js/motions/diff.js',
'openslides/core/static/js/core/pdf.js',
'tests/karma/*/*.test.js' 'tests/karma/*/*.test.js'
], ],