nativescript-community / ui-label

Alternative to the built-in NativeScript Label but with better performance and additional features such as HTML rendering and more.
Apache License 2.0
19 stars 7 forks source link

Ampersand (&) results in incorrect rendering of HTML #18

Open dangrima90 opened 1 year ago

dangrima90 commented 1 year ago

If the demo apps cannot help and there is no issue for your problem, tell us about it

Demo via NS Preview: https://stackblitz.com/edit/nativescript-stackblitz-templates-ipbbwj?file=app%2Fcomponents%2FHome.vue

Which platform(s) does your issue occur on?

Please, tell us how to recreate the issue in as much detail as possible.

The issue is that HTML is not rendered properly when including ampersands in the text. Example:

<HtmlLabel :html="'<h1>T&Cs</h1>'" /> <!-- this is rendered as HTML (i.e. '<h1>T&Cs</h1>') -->
<HtmlLabel :html="'<h1>T and Cs</h1>'" /> <!-- works as expected -->

I've tried using HTML Entities instead of the & symbol on its own but it resulted in the same thing.

<HtmlLabel :html="'<h1>T&amp;Cs</h1>'" /> <!-- this is rendered as HTML (i.e. '<h1>T&Cs</h1>') -->

I've included other examples in the NS Preview link above.

farfromrefug commented 1 year ago

@dangrima90 i dont understand your issue. What you describe seems ok to me. Please clarify what you see and what you expect

dangrima90 commented 1 year ago

@farfromrefug maybe I did not explain myself properly. The issue is that when having an & in the text HTML is not rendered as expected.

Code:

<HtmlLabel :html="`<h1>T&Cs</h1>`" linkColor="blue" />
<HtmlLabel :html="`<h1>T&amp;Cs</h1>`" linkColor="blue" />
<HtmlLabel :html="`<h1>T and Cs</h1>`" linkColor="blue" />
<HtmlLabel :html="`<span>T&Cs</span>`" linkColor="blue" />
<HtmlLabel :html="`<span>T&amp;Cs</span>`" linkColor="blue" />
<HtmlLabel :html="`<span>T and Cs</span>`" linkColor="blue" />
<HtmlLabel
  :html="`<a href='https://www.google.com'>T&Cs</a>`"
  linkColor="blue"
/>
<HtmlLabel
  :html="`<a href='https://www.google.com'>T&amp;Cs</a>`"
  linkColor="blue"
/>
<HtmlLabel
  :html="`<a href='https://www.google.com'>T and Cs</a>`"
  linkColor="blue"
/>

Screenshot:

The text you see highlighted with the red box appear as raw HTML rather than rendered as HTML.

farfromrefug commented 1 year ago

@dangrima90 i get it now thanks!

dangrima90 commented 1 year ago

Hi @farfromrefug any updates on this please?

farfromrefug commented 1 year ago

@dangrima90 no sorry it is not on my priority list right now as it can for now be fixed with a text replace. if you want to make a a PR to fix the right way that would be welcome

dangrima90 commented 1 year ago

@farfromrefug I've tested this out with the repo's demo and I can verify the following:

  1. The issue is only replicable on Android
  2. From my tests it seems that &, < and &nbsp; break the rendering
  3. Changing the above to &amp;, &lt; and (space) does result in correct rendering

To test I've extracted all the characters listed here (https://www.freeformatter.com/html-entities.html) and tested them out in the Base.vue file.

Code Snippet


My small worry is that even though I tested a lot of different symbols, I don't know if there is anything else that would break the rendering. From my end in the app I can replace the breaking symbols (&, < and &nbsp;) to make sure that the app doesn't break - but of course if there's any other symbol that breaks the rendering in the app will result in "raw HTML" being displayed.

I have tried looking at the code to understand where the error might be coming from but I'm not sure. My guess is that it is linked to the logic in here: https://github.com/nativescript-community/text/blob/0cf16bfa047e15879e49ea1e3e22fb0ac57c924e/packages/text/platforms/android/java/com/nativescript/text/Font.java#L312.

The stringBuilderFromHtmlString is called to generate the string to render, which in turn calls fromHtml. I couldn't figure out which part of the code is triggering the error.

farfromrefug commented 1 year ago

@dangrima90 thanks for investigating this. The issue clearly comes from the html parser https://github.com/nativescript-community/text/blob/0cf16bfa047e15879e49ea1e3e22fb0ac57c924e/packages/text/platforms/android/java/com/nativescript/text/HtmlToSpannedConverter.java. It is modified code based on what you can see on the web. Clearly & and < (mostly > too) breaks the parser. Not sure why though

farfromrefug commented 1 year ago

@dangrima90 stumbled upon this today. https://github.com/smart-fun/XmlToJson/issues/24 Can you try escaping the special characters <>&?

dangrima90 commented 1 year ago

@farfromrefug thanks for the above - I have been testing with HTML Entities names and got somewhere but not 100% a full proof solution. For some reason in ns preview using &amp; (&) still results in an issue, however using the HTML Entity number instead &#38; does work successfully. (https://stackblitz.com/edit/nativescript-stackblitz-templates-ipbbwj?file=app%2Fcomponents%2FHome.vue). Locally I didn't see any difference between HTML Entity names (&amp;) or numbers (&#38;) using the ui-label demo.

For my use case I've been trying to use a regex to handle the replacing of these characters. The idea was to simply find &, < and > and convert them to their HTML Entity equivalent. With simple strings this will work but when dealing with nested HTML elements things start to get weird 😄 because you end up replacing the < / > of the tags themselves which will obviously break things. So I'm sure that the best thing to use is an HTML Parser.

Similar to the stackoverflow post in the issue you linked above, I found a similar explanation re: XML invalid characters: https://stackoverflow.com/a/28152666. Long story short it mentions that &, <, >, ', " between tags should be escaped. Now from my tests only & and < resulted in an error, but I guess if there's a fix to be made it would make sense to cater for all characters that should be escaped in XML.

My question would be this: Not sure if it would be possible that whilst building the HTML string (https://github.com/nativescript-community/text/blob/0cf16bfa047e15879e49ea1e3e22fb0ac57c924e/packages/text/platforms/android/java/com/nativescript/text/HtmlToSpannedConverter.java) it would check for any characters that should be escaped and it would handle that accordingly.

Just an educated guess here, maybe something like the following will cater for this issue?

@Override
public void characters(char ch[], int start, int length) throws SAXException {
    StringBuilder sb = new StringBuilder();
    /*
        * Ignore whitespace that immediately follows other whitespace; newlines count
        * as spaces.
        */
    for (int i = 0; i < length; i++) {
        char c = ch[i + start];
        if (c == ' ' || c == '\n') {
            char pred;
            int len = sb.length();
            if (len == 0) {
                len = mSpannableStringBuilder.length();
                if (len == 0) {
                    pred = '\n';
                } else {
                    pred = mSpannableStringBuilder.charAt(len - 1);
                }
            } else {
                pred = sb.charAt(len - 1);
            }
            if (pred != ' ' && pred != '\n') {
                sb.append(' ');
            }
        } else {
            switch(c) {
                case "&":
                    sb.append("&#38;");
                    break;
                case "<":
                    sb.append("&#60;");
                    break;
                case ">":
                    sb.append("&#62;");
                    break;
                case "'":
                    sb.append("&#8217;");
                    break;
                case "\"":
                    sb.append("&#34;");
                    break;
                default:
                    sb.append(c);
                    break;
            }
        }
    }
    mSpannableStringBuilder.append(sb);
}