milkshakesoftware/PreMailer.Net

MoveCssInline returns self-closing title when empty

papafe opened this issue · 5 comments

When the original html has an empty title, MoveCssInline returns an html with a self-closing title, that make the page not work on browsers.

For example, take in consideration the following HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
</head>
<body>
    <p>
        Test
    </p>
</body>
</html>

This is the result of MoveCssInline:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title />
</head>
<body>
    <p>
        Test
    </p>
</body></html>

Tested on PreMailer 2.1.3

Nevca commented

I have the same issue. When is this planned to be fixed?

This is the default way AngeSharp spits out the HTML.

You may want to take a look at the latest version, and utilize the ability to provide your own IMarkupFormatter as described in #198.

Default HtmlFormatter from AngleSharp can be found here: https://github.com/AngleSharp/AngleSharp/blob/master/src/AngleSharp/Html/HtmlMarkupFormatter.cs

But if you don't mind, please advise if/which e-mail clients have difficulties dealing with self closed title tag?

Nevca commented

Thank you for pointing me to IMarkupFormatter, I am using version 2.1.3 and it's not available there. I will upgrade and try it out.

The issue is not with any email client specifically but with the fact that some browsers do not show the page's content when the title tag is shortened, as it is not per HTML standards. I tested it with Chrome 81.0.4044.122 and Firefox 75.0. Outlook and eM Client also do not show the content in this case.

We have just faced the same problem - that MoveCssInline produces invalid HTML.
Approach with IMarkupFormatter works.
As I did not find any example on the actual implementation let me share my code here:

var pm = new PreMailer.Net.PreMailer(html);
var defaultMarkupFormatter =  
	pm.Document != null 
	&& pm.Document.Doctype != null 
	&& pm.Document.Doctype.PublicIdentifier != null 
	&& pm.Document.Doctype.PublicIdentifier.Contains("XHTML") 
		? XhtmlMarkupFormatter.Instance 
		: HtmlMarkupFormatter.Instance;
IMarkupFormatter formatter = new MarkupFormatter(defaultMarkupFormatter);
var moveCssInlineResult = pm.MoveCssInline(customFormatter: formatter);
return moveCssInlineResult.Html;

And the MarkupFormatter itself is implemented as a decorator:

public class MarkupFormatter : IMarkupFormatter
{
	private IMarkupFormatter _markupFormatter;

	public MarkupFormatter(IMarkupFormatter markupFormatter)
	{
		_markupFormatter = markupFormatter;
	}

	public string Text(ICharacterData text) => _markupFormatter.Text(text);

	public string LiteralText(ICharacterData text) => _markupFormatter.LiteralText(text);

	public string Comment(IComment comment) => _markupFormatter.Comment(comment);

	public string Processing(IProcessingInstruction processing) => _markupFormatter.Processing(processing);

	public string Doctype(IDocumentType doctype) => _markupFormatter.Doctype(doctype);

	public string OpenTag(IElement element, bool selfClosing)
	{
		if (element is IHtmlTitleElement title
			&& string.IsNullOrWhiteSpace(title.Text))
			return title.OuterHtml;
		return _markupFormatter.OpenTag(element, selfClosing);
	}

	public string CloseTag(IElement element, bool selfClosing)
	{
		if (element is IHtmlTitleElement title
			&& string.IsNullOrWhiteSpace(title.Text))
			return string.Empty;
		return _markupFormatter.CloseTag(element, selfClosing);
	}
}