@@ -350,6 +350,7 @@ class HtmlToSpannedConverter implements ContentHandler {
350350 private final EnrichedParser .ImageGetter mImageGetter ;
351351 private static Integer currentOrderedListItemIndex = 0 ;
352352 private static Boolean isInOrderedList = false ;
353+ private static Boolean isEmptyTag = false ;
353354
354355 public HtmlToSpannedConverter (String source , HtmlStyle style , EnrichedParser .ImageGetter imageGetter , Parser parser ) {
355356 mStyle = style ;
@@ -396,19 +397,27 @@ public Spanned convert() {
396397 for (EnrichedZeroWidthSpaceSpan zeroWidthSpaceSpan : zeroWidthSpaceSpans ) {
397398 int start = mSpannableStringBuilder .getSpanStart (zeroWidthSpaceSpan );
398399 int end = mSpannableStringBuilder .getSpanEnd (zeroWidthSpaceSpan );
399- mSpannableStringBuilder .insert (start , "\u200B " );
400+
401+ if (mSpannableStringBuilder .charAt (start ) != '\u200B' ) {
402+ // Insert zero-width space character at the start if it's not already present.
403+ mSpannableStringBuilder .insert (start , "\u200B " );
404+ end ++; // Adjust end position due to insertion.
405+ }
406+
400407 mSpannableStringBuilder .removeSpan (zeroWidthSpaceSpan );
401- mSpannableStringBuilder .setSpan (zeroWidthSpaceSpan , start , end + 1 , Spannable .SPAN_EXCLUSIVE_EXCLUSIVE );
408+ mSpannableStringBuilder .setSpan (zeroWidthSpaceSpan , start , end , Spannable .SPAN_EXCLUSIVE_EXCLUSIVE );
402409 }
403410
404411 return mSpannableStringBuilder ;
405412 }
406413
407414 private void handleStartTag (String tag , Attributes attributes ) {
415+ isEmptyTag = false ;
408416 if (tag .equalsIgnoreCase ("br" )) {
409417 // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>
410418 // so we can safely emit the linebreaks when we handle the close tag.
411419 } else if (tag .equalsIgnoreCase ("p" )) {
420+ isEmptyTag = true ;
412421 startBlockElement (mSpannableStringBuilder );
413422 } else if (tag .equalsIgnoreCase ("ul" )) {
414423 isInOrderedList = false ;
@@ -418,14 +427,17 @@ private void handleStartTag(String tag, Attributes attributes) {
418427 currentOrderedListItemIndex = 0 ;
419428 startBlockElement (mSpannableStringBuilder );
420429 } else if (tag .equalsIgnoreCase ("li" )) {
430+ isEmptyTag = true ;
421431 startLi (mSpannableStringBuilder );
422432 } else if (tag .equalsIgnoreCase ("b" )) {
423433 start (mSpannableStringBuilder , new Bold ());
424434 } else if (tag .equalsIgnoreCase ("i" )) {
425435 start (mSpannableStringBuilder , new Italic ());
426436 } else if (tag .equalsIgnoreCase ("blockquote" )) {
437+ isEmptyTag = true ;
427438 startBlockquote (mSpannableStringBuilder );
428439 } else if (tag .equalsIgnoreCase ("codeblock" )) {
440+ isEmptyTag = true ;
429441 startCodeBlock (mSpannableStringBuilder );
430442 } else if (tag .equalsIgnoreCase ("a" )) {
431443 startA (mSpannableStringBuilder , attributes );
@@ -632,11 +644,17 @@ private static void setSpanFromMark(Spannable text, Object mark, Object... spans
632644 }
633645 }
634646
635- private static void setParagraphSpanFromMark (Spannable text , Object mark , Object ... spans ) {
647+ private static void setParagraphSpanFromMark (Editable text , Object mark , Object ... spans ) {
636648 int where = text .getSpanStart (mark );
637649 text .removeSpan (mark );
638650 int len = text .length ();
639651
652+ // Block spans require at least one character to be applied.
653+ if (isEmptyTag ) {
654+ text .append ("\u200B " );
655+ len ++;
656+ }
657+
640658 // Adjust the end position to exclude the newline character, if present
641659 if (len > 0 && text .charAt (len - 1 ) == '\n' ) {
642660 len --;
@@ -741,6 +759,8 @@ public void endElement(String uri, String localName, String qName) {
741759
742760 public void characters (char [] ch , int start , int length ) {
743761 StringBuilder sb = new StringBuilder ();
762+ if (length > 0 ) isEmptyTag = false ;
763+
744764 /*
745765 * Ignore whitespace that immediately follows other whitespace;
746766 * newlines count as spaces.
0 commit comments