Merge pull request #4291 from CatoTH/pdf-split-inline-elements-at-line-break

Provide a method to split inline elements at line breaks
This commit is contained in:
Sean 2019-02-11 13:05:09 +01:00 committed by GitHub
commit cda5e066de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 0 deletions

View File

@ -774,6 +774,31 @@ describe('LinenumberingService', () => {
}));
});
describe('adapting html for pdf generation', () => {
it('splits inline tags', inject([LinenumberingService], (service: LinenumberingService) => {
const inHtml =
'<ul><li><p><span class="test"><strong>' +
noMarkup(1) +
'Line 1' +
brMarkup(2) +
'<em>Line 2' +
brMarkup(3) +
'Line 3</em>' +
'</strong></span></p></li></ul>';
const stripped = service.splitInlineElementsAtLineBreaks(inHtml);
expect(stripped).toBe(
'<ul><li><p>' +
noMarkup(1) +
'<span class="test"><strong>Line 1</strong></span>' +
brMarkup(2) +
'<span class="test"><strong><em>Line 2</em></strong></span>' +
brMarkup(3) +
'<span class="test"><strong><em>Line 3</em></strong></span>' +
'</p></li></ul>'
);
}));
});
describe('caching', () => {
it('caches based on line length', inject([LinenumberingService], (service: LinenumberingService) => {
const inHtml = '<p>' + longstr(100) + '</p>';

View File

@ -1027,4 +1027,68 @@ export class LinenumberingService {
return html;
}
/**
* Helper function that does the actual work for `splitInlineElementsAtLineBreaks`
*
* @param {Element} lineNumber
*/
private splitInlineElementsAtLineBreak(lineNumber: Element): void {
const parentIsInline = (el: Element) => this.isInlineElement(el.parentElement);
while (parentIsInline(lineNumber)) {
const parent: Element = lineNumber.parentElement;
const beforeParent: Element = <Element>parent.cloneNode(false);
// If the node right before the line number is a line break, move it outside along with the line number
let lineBreak: Element = null;
if (this.isOsLineBreakNode(lineNumber.previousSibling)) {
lineBreak = <Element>lineNumber.previousSibling;
lineBreak.remove();
}
// All nodes before the line break / number are moved into beforeParent
while (lineNumber.previousSibling) {
const previousSibling = lineNumber.previousSibling;
parent.removeChild(previousSibling);
if (beforeParent.childNodes.length > 0) {
beforeParent.insertBefore(previousSibling, beforeParent.firstChild);
} else {
beforeParent.appendChild(previousSibling);
}
}
// Insert beforeParent before the parent
if (beforeParent.childNodes.length > 0) {
parent.parentElement.insertBefore(beforeParent, parent);
}
// Add the line number and (if found) the line break inbetween
if (lineBreak) {
parent.parentElement.insertBefore(lineBreak, parent);
}
parent.parentElement.insertBefore(lineNumber, parent);
}
}
/**
* This splits all inline elements so that each line number (including preceding line breaks)
* are located directly in a block-level element.
* `<p><span>...[linebreak]...</span></p>`
* is therefore converted into
* `<p><span>...</span>[linebreak]<span>...</span></p>
*
* This function is mainly provided for the PDF generation
*
* @param {string} html
* @returns {string}
*/
public splitInlineElementsAtLineBreaks(html: string): string {
const fragment = this.htmlToFragment(html);
const lineNumbers = fragment.querySelectorAll('span.os-line-number');
lineNumbers.forEach((lineNumber: Element) => {
this.splitInlineElementsAtLineBreak(lineNumber);
});
return this.fragmentToHtml(fragment);
}
}