From ed62c172fb809d40e4a6940a4fd74be847582824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Ho=CC=88=C3=9Fl?= Date: Sat, 9 Feb 2019 19:30:56 +0100 Subject: [PATCH] Provide a method to split inline elements at line breaks --- .../ui-services/linenumbering.service.spec.ts | 25 ++++++++ .../core/ui-services/linenumbering.service.ts | 64 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/client/src/app/core/ui-services/linenumbering.service.spec.ts b/client/src/app/core/ui-services/linenumbering.service.spec.ts index 8ac85cf20..6c73938f5 100644 --- a/client/src/app/core/ui-services/linenumbering.service.spec.ts +++ b/client/src/app/core/ui-services/linenumbering.service.spec.ts @@ -774,6 +774,31 @@ describe('LinenumberingService', () => { })); }); + describe('adapting html for pdf generation', () => { + it('splits inline tags', inject([LinenumberingService], (service: LinenumberingService) => { + const inHtml = + ''; + const stripped = service.splitInlineElementsAtLineBreaks(inHtml); + expect(stripped).toBe( + '' + ); + })); + }); + describe('caching', () => { it('caches based on line length', inject([LinenumberingService], (service: LinenumberingService) => { const inHtml = '

' + longstr(100) + '

'; diff --git a/client/src/app/core/ui-services/linenumbering.service.ts b/client/src/app/core/ui-services/linenumbering.service.ts index 4fe81e352..dd1069201 100644 --- a/client/src/app/core/ui-services/linenumbering.service.ts +++ b/client/src/app/core/ui-services/linenumbering.service.ts @@ -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 = 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 = 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. + * `

...[linebreak]...

` + * is therefore converted into + * `

...[linebreak]...

+ * + * 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); + } }