diff --git a/packages/parchment/lib/src/codecs/html.dart b/packages/parchment/lib/src/codecs/html.dart index edb0e18d..9f3907e8 100644 --- a/packages/parchment/lib/src/codecs/html.dart +++ b/packages/parchment/lib/src/codecs/html.dart @@ -273,12 +273,22 @@ class _ParchmentHtmlEncoder extends Converter { // Closing tags effectively adds the opening tag at the appropriate position // AND adds the closing tag final attributesToRemove = <_HtmlInlineTag>{}; - for (final attr in openInlineTags) { + for (var i = 0; i < openInlineTags.length; i++) { + final attr = openInlineTags[i]; if (!inlineAttributes.contains(attr.attribute)) { + // remove any tag that was opened later as they must be closed + for (var j = 0; j <= i; j++) { + final prevAttr = openInlineTags[j]; + if (prevAttr.openingPosition > attr.openingPosition) { + _writeTag(buffer, prevAttr); + attributesToRemove.add(prevAttr); + } + } _writeTag(buffer, attr); attributesToRemove.add(attr); } } + for (final attr in attributesToRemove) { openInlineTags.remove(attr); } diff --git a/packages/parchment/test/codecs/html_test.dart b/packages/parchment/test/codecs/html_test.dart index f3cf4e1d..dd09632f 100644 --- a/packages/parchment/test/codecs/html_test.dart +++ b/packages/parchment/test/codecs/html_test.dart @@ -101,6 +101,27 @@ void main() { 'Something in the way mmmm...'); }); + test('tangled inline tags', () { + final doc = ParchmentDocument.fromJson([ + {'insert': 'AAA'}, + { + 'insert': 'BB', + 'attributes': {'b': true} + }, + { + 'insert': 'B', + 'attributes': {'b': true, 's': true} + }, + { + 'insert': 'CCC', + 'attributes': {'s': true} + }, + {'insert': '\n'} + ]); + expect(codec.encode(doc), + 'AAABBBCCC'); + }); + test('html escaping', () { final doc = ParchmentDocument.fromJson([ { @@ -1204,6 +1225,27 @@ void main() { expect(codec.decode(html).toDelta(), doc.toDelta()); }); + test('tangled inline tags', () { + final html = 'AAABBBCCC'; + final doc = ParchmentDocument.fromJson([ + {'insert': 'AAA'}, + { + 'insert': 'BB', + 'attributes': {'b': true} + }, + { + 'insert': 'B', + 'attributes': {'b': true, 's': true} + }, + { + 'insert': 'CCC', + 'attributes': {'s': true} + }, + {'insert': '\n'} + ]); + expect(codec.decode(html).toDelta(), doc.toDelta()); + }); + test('embedded inline attributes text', () { final html = '

Something in the way mmmm...

';