Code Monkey home page Code Monkey logo

premailer.net's Introduction

PreMailer.Net .NET Core build Nuget count

C# Library for moving CSS to inline style attributes, to gain maximum E-mail client compatibility.

Usage

Static method on PreMailer class

string htmlSource = File.ReadAllText(@"C:\Workspace\testmail.html");

var result = PreMailer.MoveCssInline(htmlSource);

result.Html 		// Resultant HTML, with CSS in-lined.
result.Warnings 	// string[] of any warnings that occurred during processing.

Set up PreMailer instance

string htmlSource = File.ReadAllText(@"C:\Workspace\testmail.html");

var pm = new PreMailer(htmlSource);
pm.AddAnalyticsTags(source, medium, campaign, content, domain = null); // Optional to add analytics tags

var result = pm.MoveCssInline(...);

result.Html 		// Resultant HTML, with CSS in-lined.
result.Warnings 	// string[] of any warnings that occurred during processing.

Options

The following options can be passed to the PreMailer.MoveCssInline method to configure it's behaviour:

  • baseUri(Uri = null) - Base URL to apply to link elements with href values ending with .css.
  • removeStyleElements(bool = false) - Removes elements that were used to source CSS (currently, only style is supported).
  • ignoreElements(string = null) - CSS selector of element(s) not to inline. Useful for mobile styles (see below).
  • css(string = null) - A string containing a style-sheet for inlining.
  • stripIdAndClassAttributes(bool = false) - True to strip ID and class attributes.
  • removeComments(bool = false) - True to remove comments, false to leave them intact.

External style sheets

Sometimes it's handy to reference external style sheets with a <link href="..." /> element. PreMailer will download and use external style sheets as long as the value of href ends with .css.

Both absolute and relative URLs are supported. If the URL is relative, you must specify the baseUri parameter in either the constructor, or when calling the static MoveCssInline method.

<link /> elements that match the ignoreElements selector won't be downloaded.

Media queries

If you want to apply mobile styles to your e-mail, you should put your mobile specific styles in its own style block that targets the appropriate devices using media queries.

But since you cannot know by the time of sending an e-mail whether or not it will be viewed on a mobile device, the style block that targets mobile devices should not be inlined!

To ignore a style block, you need to specify an ignore selector when calling the MoveCssInline method, like this:

var result = PreMailer.MoveCssInline(input, false, ignoreElements: "#ignore");

And your mobile specific style block should have an ID of ignore:

<style type="text/css" id="ignore">.target { width: 1337px; }</style>

Premailer specific CSS becomes HTML attributes

Premailer looks for the use of CSS attributes prefixed with -premailer and will proxy the value through to the DOM element as an attribute.

For example

table {
    -premailer-cellspacing: 5;
    -premailer-width: 500;
}

will make a table element render as

<table cellspacing="5" width="500">

Custom DOM Processing

using(var pm = new PreMailer(html)){

  var document = pm.Document;

  // use AngleSharp to process document before moving css inline ...

  var result = pm.MoveCssInline();
}

Notes

  • Pseudo classes/elements which not supported by external dependencies, or doesn't make sense in email, will be ignored and logged to the InlineResult.Warnings collection.

Installation

NuGet: PreMailer.Net

Contributors

Among others

License

PreMailer.Net is available under the MIT license. See the LICENSE file for more info.

premailer.net's People

Contributors

axunonb avatar benedictebook avatar codeanimal avatar colefichter avatar dependabot-preview[bot] avatar dependabot[bot] avatar ejsmith avatar github-actions[bot] avatar jan-michaelwilliams avatar jasekiw avatar jdpurcell avatar joe-camp-nelnet avatar kendallb avatar leniency avatar martinnormark avatar mstijak avatar patrikwlund avatar pfeurean avatar r-larch avatar richardwright avatar robcthegeek avatar robhoward avatar sfjoe avatar simoncropp avatar skolima avatar sleeplessghost avatar styfle avatar tooppaaa avatar vikranthc avatar zeus82 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

premailer.net's Issues

support compression/minification as part of premailer.net run

This might not make sense, but at the moment it seems like something that would be useful as part of the premailer run (and I think more useful than having callers run it before or after) is including the YUI Compression.

In particular, the .NET port that's on NuGet would seem to be a good fit:

http://nuget.org/packages/YUICompressor.NET

http://yuicompressor.codeplex.com/

Not sure if NuGet supports an 'optional dependency' such that if the user chooses to ask PreMailer.Net to include compression, it could either do so if YUICompressor.NET is already installed, or give an error asking to have the user to install it if it's not (bonus points if it can install it as needed, but probably not support :)

New release with AngelSharp

Using the 1.4.3 release (via nuget) I get the error below, but it looks like it would already be fixed?... because CsQuery is no longer used:

5912374

...So wondering when next release will be? :-)

System.NullReferenceException was unhandled
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=CsQuery
StackTrace:
at CsQuery.Engine.SelectorEngine.b__36(IDomObject item)
at CsQuery.CQ.d__101.MoveNext() at CsQuery.Engine.SelectorEngine.Select(IEnumerable1 context)
at CsQuery.CQ.Select(String selector)
at PreMailer.Net.PreMailer.FindElementsWithStyles(SortedList`2 stylesToApply)
at PreMailer.Net.PreMailer.MoveCssInline(Boolean removeStyleElements, String ignoreElements, String css, Boolean stripIdAndClassAttributes, Boolean removeComments)
at PreMailer.Net.PreMailer.MoveCssInline(String html, Boolean removeStyleElements, String ignoreElements, String css, Boolean stripIdAndClassAttributes, Boolean removeComments)
at EE.AskForReviews.ProgramToAskForReviews.Main(String[] args) in D:\Projects\ExpertEasy\ExpertEasy\MAIN\Source\EE.AskForReviews\ProgramToAskForReviews.cs:line 75
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:

css gradients and the argument for inline all iterations of a style

Hi

I was wondering how we might go about getting PreMailer to inline all occurences of a particular style, rather than just one?

For example: This is the syntax for a gradient.

element {

background: -moz-linear-gradient(black, white); /* FF 3.6+ */  
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #000000), color-stop(100%, #ffffff)); /* Safari 4+, Chrome 2+ */  
background: -webkit-linear-gradient(black, white); /* Safari 5.1+, Chrome 10+ */  
background: -o-linear-gradient(black, white); /* Opera 11.10 */  
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000', endColorstr='#ffffff'); /* IE6 & IE7 */  
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000', endColorstr='#ffffff')"; /* IE8+ */  
background: linear-gradient(black, white); /* the standard */  

}

The Renderer won't recognise most of these, but because of the specificity of having the wrong one inline, it just defaults what is inline rather than looking for any more css, such as something in the head or an external file. For css gradients it would therefore be useful to include all the declarations.

Any thoughts anyone on this?

Pass the content of a CSS file via PremailerNet API

If there would be a possibility to pass the content of a CSS file via PremailerNet API, you would:
-get rid of relative/absolute url links
-and we would get a possibility to use a single CSS to style every mail output...

Create Strongly-Typed Result for 'MoveCssInline'

As discussed here - ideally we should have much richer output from the MoveCssInline method.

Current ideas are:

  • HTML (with CSS in-lined).
  • Text-only content
  • Warnings that occurred during processing.
  • Errors that occurred during processing.

I'm thinking we could then easily consume this class in other utility methods (one that springs to mind it something like result.ToMailMessage(to,from, subject) to construct a MailMessage class from the result for sending.

NOTE: It's best if we remove the HTML comment generation that currently takes place if there are pseudo classes/elements in the CSS. See here.

Add support for pseudo-classes

This could be done by collecting all the pseudo-class styles found and then appending a new style element with all the pseudo-classes that cannot be placed inline to the end of the head tag.

I guess this would only be necessary if the RemoveStyleElements parameter is True.

PreMailer wrongly strips HTML comments from STYLE blocks

Hi,

I know this is a bit niche, but legacy systems have got me to the point where I need to convert the following HTML.

The comments inside the style tags should be ignored when processing them as CSS (and this file in a browser will show as bold), but PreMailer is ignoring them or stripping them out when remove comments is enabled, so the preMailer output is different to how the source views in a browser.

<html>
    <head>
        <style>
        <!--
        p.custom {
        font-weight: bold;
        }
        -->
        </style>
    </head>
    <body>
        <div>
            <p class="custom">Hello World!</p>
        </div>
    </body>
</html>

CSS specificity bug

Hi

I've just noticed that the following CSS doesn't produce the inline styles as it should:

p,
li,
tr.pub-heading td,
tr.pub-footer td,
tr.footer-heading td {
    font-size: 12px;
    line-height: 16px;
}
td.disclaimer p {font-size: 11px;} 

PreMailer incorrectly outputs 12px for the 'td.disclaimer p' selector. It looks like the specificity is being added up wrong. The comma separated values should be given a specificity for each value.

One thing I found works is to split these up in the CssParser FillStyleClass method:

        private void FillStyleClass(string s)
        {
            string[] parts = s.Split('{');
            var cleaned = CleanUp(parts[0]).Trim();
            var styleNames = cleaned.Split(',').Select(x => x.Trim());

            foreach (var styleName in styleNames)
            {
                StyleClass sc;
                if (_scc.ContainsKey(styleName))
                {
                    sc = _scc[styleName];
                    _scc.Remove(styleName);
                }
                else
                {
                    sc = new StyleClass();
                }

                FillStyleClass(sc, styleName, parts[1]);

                _scc.Add(sc.Name, sc);
            }
        }

What do you think?

Cheers
j055

Styles In Second 'STYLE' Tag Are Not Applied Correctly

Hey @martinnormark - been doing some more testing of this and come across another issue (may have been caused by me in the last update 😊 - I've not checked).

When we ensure that existing in-line styles are honoured, we never able to apply styles from a second style tag, since we've already in-lined those from the first.

Failing Test

[TestMethod]
public void MoveCssInline_CssWithHigherSpecificityInSeparateStyleTag_AppliesMoreSpecificCss()
{
  string input = "<html><head><style type=\"text/css\">.target { width: 42px; }</style><style type=\"text/css\">.outer .inner .target { width: 1337px; }</style></head><body><div class=\"outer\"><div class=\"inner\"><div class=\"target\">test</div></div></div></body></html>";

  string premailedOutput = sut.MoveCssInline(input, false);

  Assert.IsTrue(premailedOutput.Contains("style=\"width: 1337px;\""));
}

I'm already working on the fix.

Media tags are removed?

I am trying to use Premailer.NET to create responsive newsletters. However, @media tags seems to get stripped away. Where do they go?

PreMailer.net is unable to process the pseudo class/element due to limitation in CsQuery

Hi there

I just downloaded the Premailer.NET today and build in VS2013, when I try to do a simple call like
string htmlSource = System.IO.File.ReadAllText(@"c:\sidebar-hero.html");
var result = PreMailer.MoveCssInline(htmlSource)

on a ZURB INK template sidebar-hero.html (http://zurb.com/ink/downloads/templates/sidebar-hero.html )

I get the warning as following:
PreMailer.Net is unable to process the pseudo class/element 'a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'a:hover' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h1 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h1 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h2 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h2 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h3 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h3 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h4 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h4 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h5 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h5 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h6 a:active' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'h6 a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.alert:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:active td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:visited td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.button:visited td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.facebook:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.google-plus:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.large-button td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.large-button:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.large-button:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.large-button:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.medium-button td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.medium-button:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.medium-button:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.medium-button:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.secondary td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.secondary:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.secondary:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.secondary:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.small-button td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.small-button:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.small-button:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.small-button:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.success:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.tiny-button td a:visited' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.tiny-button:active td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.tiny-button:hover td' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.tiny-button:hover td a' due to a limitation in CsQuery.

PreMailer.Net is unable to process the pseudo class/element 'table.twitter:hover td' due to a limitation in CsQuery.

Premailer preserves !important in css rules.

If I have this input:

<html>
  <head>
    <style type="text/css">
      .foo
      {
         width: 300px !important;
      }
    </style>
  </head>
  <body>
    <div class="foo" style="width: 200px"></div>
  </body>
</html>

premailer generates this output:

<html><head>

  </head>
  <body>
    <div class="foo" style="width: 300px !important"></div>

</body></html>

However, is there a way to instead have this html generated?

<html><head>

  </head>
  <body>
    <div class="foo" style="width: 300px"></div>

</body></html>

!important is not needed any longer in inline styles, is it?

Any plans to move away from CSQuery?

Are there any plans to move away from CSQuery and maybe use AngleSharp instead? Would there be any interest if i would start working on a pull request that replaces CSQuery with AngleSharp?

Is it possible to strip "class" and "id" attributes?

There is an option for removing style blocks after processing. What about class and id attributes?

It is much easier to target elements this way, but of course that means that once the CSS is inlined, those attributes remain behind. Can they be removed somehow?

Premailed uses CsQuery, which is similar to jQuery. If it were jQuery I'd do a *[class] and *[id] and then each(function() { ... for them and then (this).removeAttr("class") and (this).removeAttr("id"). Maybe that's a start?

Certain characters in attribute values are not being escaped resulting in invalid HTML

Characters that should be escaped are not escaped when they occur in attribute values.
That is, a valid HTML5 document (e.g., per https://html5.validator.nu/) can be passed to PreMailer.Net and come out as an invalid HTML5 document.

I am using:
PreMailer.Net 1.4.2.0
CsQuery 1.3.3249 (also reproduces with CsQuery trunk)

I think this is a bug in CsQuery itself, but you should be aware in case you want to fix CsQuery or workaround this some way. Not sure, but suspect that because this affects escaping, this may be a security problem where undesired HTML can be injected.

Program:

using System;

namespace PreMailerTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string h = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>test</title></head><body><a href=\"http://www.example.com?a=1&amp;b=2\">Hello &amp; World</a></body></html>";
            Console.WriteLine("Valid per https://html5.validator.nu/:");
            Console.WriteLine(h);
            Console.WriteLine("Invalid per https://html5.validator.nu/:");
            Console.WriteLine(PreMailer.Net.PreMailer.MoveCssInline(h).Html);
        }
    }
}

Output:

Valid per https://html5.validator.nu/:
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>test</title></head><body><a href="http://www.example.com?a=1&amp;b=2">Hello &amp; World</a></body></html>
Invalid per https://html5.validator.nu/:
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>test</title></head><body><a href="http://www.example.com?a=1&b=2">Hello &amp; World</a></body></html>

Message from validator:

Error: & did not start a character reference. (& probably should have been escaped as &amp;.)
At line 1, column 117
example.com?a=1&b=2">Hello &am

Error parsing bootstrap css

I am getting an error when tring to parse bootstrap css framework.

The string did not match the expected pattern.
at AngleSharp.Extensions.QueryExtensions.QuerySelectorAll(INodeList elements, String selectors)
at AngleSharp.Dom.Document.QuerySelectorAll(String selectors)
at PreMailer.Net.PreMailer.FindElementsWithStyles(SortedList`2 stylesToApply) in PreMailer.cs:line 305
at PreMailer.Net.PreMailer.MoveCssInline(Boolean removeStyleElements, String ignoreElements, String css, Boolean stripIdAndClassAttributes, Boolean removeComments) in PreMailer.cs:line 104
at PreMailer.Net.PreMailer.MoveCssInline(String html, Boolean removeStyleElements, String ignoreElements, String css, Boolean stripIdAndClassAttributes, Boolean removeComments) in PreMailer.cs:line 51

Do you have any idea?

Browser-specific selector modifiers throw exception

Some examples:

button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}

.form-control:-moz-placeholder {
color: #999999;
}

Error message:
Unexpected character found at position 15: ".. m-control::>>-<<moz-placeholder"

Note: Although these are primarily form-related and therefore inconsequential to emails, it should not throw an exception while trying to parse these.

References external CSS files

This suggestion was reported originally by Rick Dorris on the comments section of your article (http://martinnormark.com/move-css-inline-premailer-net/) and I've searched through the issues list and didn't find nothing related to this.

So, is it possible to PreMailer process the references to external CSS files so we don't have to copy the (sometimes enormous) CSS styles to the HTML page?

Thanks

MoveCssInline removes XML namespaces

Non-default XML namespaces are removed when inlining styles. For example:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">

Turns into:

<html xmlns="http://www.w3.org/1999/xhtml">

This is a problem since the specified namespaces support workarounds for image scaling in Outlook on Windows systems with >100% font scaling. See this campaignmonitor thread for more info. Specifically, they enable the following conditional settings to apply in newer versions of Outlook:

<head>
  <!--[if gte mso 9]><xml>
   <o:OfficeDocumentSettings>
    <o:AllowPNG/>
    <o:PixelsPerInch>96</o:PixelsPerInch>
   </o:OfficeDocumentSettings>
  </xml><![endif]-->
</head>

XML namespaces were not removed in older versions of PreMailer.Net that used HtmlAgilityPack/Fizzler.

Specifying "html" in the ignoreElements argument of MoveCssInline unfortunately does not resolve the issue.

New overloaded MoveCssInline method

Could we have an overloaded MoveCssInline method to take the results of a CssParser?

InlineResult MoveCssInline(string html, SortedList<string, StyleClass> styles)

It helps when you are sending many messages all based around a single stylesheet.

Thanks
j055

Pseudo selector throws exception

Im aware that PreMailer.Net doesn't support pseudo selectors, but the following CSS will cause MoveCssInline() to throw an exception:

button::-moz-focus-inner, input[type='submit']::-moz-focus-inner { border: 0; padding: 0; }

Stack track for reference:

System.ArgumentException: Unexpected character found at position 8: ".. button::>>-< at CsQuery.StringScanner.Implementation.StringScannerEngine.ThrowException(String message, Int32 errPos) at CsQuery.StringScanner.Implementation.StringScannerEngine.Expect(Func3 validate)
at CsQuery.StringScanner.Implementation.StringScannerEngine.Get(Func3 validate) at CsQuery.Engine.SelectorParser.Parse(String selector) at CsQuery.Engine.Selector..ctor(String selector) at CsQuery.CQ.Select(String selector) at PreMailer.Net.PreMailer.FindElementsWithStyles(SortedList2 stylesToApply)
at PreMailer.Net.PreMailer.Process()`

Add Internal Stylesheet CSS to inline CSS rather than overwrite it... please...

Hi,

Great component. However it would be even greater if any inline CSS styling was retained and not overwritten by internal stylesheet CSS. Sometimes there is very bespoke CSS that is placed inline and this must not be lost. Here is an example of my thinking:

Current CSS

<style>
.test
{
font-family:calibri
}
</style>

<div class=”test” style=” width:100px”>

After Premailer should become:

<div style=” width:100px; font-family:calibri”>

And not, as is presently the case:

<div style=” font-family:calibri” >

Is this possible?

Thanks,

Ed

Property order incorrect for equal specificity (shorthand prop issue?)

Given the following style which sets all buttons blue, then makes specific buttons their desired color:

<style>
table.tiny-button td,
table.small-button td,
table.medium-button td,
table.large-button td {
  background: #2284a1;
  ...
}

...

table.google-plus td {
  background-color: #DB4A39;
  ...
}
</style>

... and the html below, before the css is moved inline. Both selectors match with the same specificity however the 'google-plus' selector occurs further down the file. I would expect the 'google-plus' style to take precedence.

<table class="tiny-button google-plus">
  <tr>
    <td>
      <a href="#">Google +</a>
    </td>
  </tr>
</table>

Below is the output, you can see that the style which occurred first is applied last inline. I presume that because 'background' != 'background-color' thus the order was not considered.

<table>
  <tbody>
    <tr>
      <td style="... background-color: #DB4A39; ... background: #2ba6cb"><a href="#">Google +</a> 
      </td>
    </tr>
  </tbody>
</table>

Doctype issues from CsQuery

Is there a workaround for this bug in CsQuery:
jamietre/CsQuery#100

I've been noticing that <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> is being transformed to <!DOCTYPE html "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> after using PreMailer.Net.PreMailer.MoveCssInline(...) and I think that bug is why it's happening. CsQuery has a pre-release package on NuGet with the fix though, have you tried it out, does it work, or is there a way I don't have to update my CsQuery package to an unstable release?

Unknown pseudo-class 'link'

When I put through html with the link pseudo-class it PreMailer,Net throws the exception:

Unknown pseudo-class 'link'. Use either first-child, last-child, only-child or empty.

Error on pseudo class

If my style has some pseudo class(:hover, :visited), it throws a error. I think it should ignore the selector instead

Add Support Pseudo Selectors Implemented by CSQuery

Currently, PreMailer will look to see if there are any pseudo selectors within the styles given, and ignore it - marking it as a warning.

This code can be seen here. This was a first step in just "getting more aware" about what was in the styles that we may/may not be able to deal with.

CsQuery does in fact support some of the pseudo classes - see "Features" here.

We should update the code in PreMailer to support these.

support the additional functionality included with the premailer ruby gem

When I saw the title of this project, I thought it might be a port of the ruby gem and got very excited :)

While inlining CSS into style attributes is definitely a requirement for html emails (and is something both projects do), there's a small number of additional things the premailer gem does that seems like they would be worth including in this project as well?

https://github.com/alexdunae/premailer

  • Relative paths are converted to absolute paths Checks links in href, src and CSS url('')
  • CSS properties are checked against e-mail client capabilities Based on the Email Standards Project’s guides
  • A plain text version is created Optional

Thanks!!

CSS Specificity is Not Honoured When Merging CSS Styles

When merging the CSS styles here, the CSS specificity of the new style definition is not being honoured, so more-specific classes that are defined later are not getting applied to the element in question.

Failing test case below:

[TestMethod]
public void MoveCssInline_UnclosedTableCell_BackgroundColourApplied()
{
  string input = @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN"" ""http://www.w3.org/TR/html4/strict.dtd""><html><head><title>Y U NO WORK?</title></head><body><div id=""content""><style>.tblGenFixed td {background-color:#fff;}.tblGenFixed td.s0 {background-color:#ead1dc;}</style><table dir='ltr' border=0 cellpadding=0 cellspacing=0 class='tblGenFixed' id='tblMain'><tr dir='ltr'><td colspan=6 dir='ltr' class='s0'>Complete My AFF Course</tr></div></body></html>";

  string premailedOutput = sut.MoveCssInline(input, false);

  Assert.IsTrue(premailedOutput.Contains("style=\"background-color: #ead1dc;\""));
}

Ambiguity with class name and root namespace

Is it just me or does anyone else have issues because the class PreMailer is also a root namespace? Ctrl+. to auto resolve namespace does not work.

Even when I have using PreMailer.Net; I still have to fully qualify the namespace as: PreMailer.Net.PreMailer.MoveCssInline(htmlSource);

instead of PreMailer.MoveCssInline(htmlSource);

MoveCssInline converts Unicode characters into HTML Unicode symbols

After invoking MoveCssInline method on a string containing Russian characters, the Html result returns a string with all Russian characters converted to HTML Unicode symbols. For example, the text

Привет!

gets converted to

&amp;#1055;&amp;#1088;&amp;#1080;&amp;#1074;&amp;#1077;&amp;#1090;!

This would be somewhat understandable behavior when saving a Unicode string in an ASCII file, but here processing is done in memory between two Unicode (UTF-16) strings (MoveCssInline takes a string as a parameter, and InlineResult.Html is a string parameter). Doesn't seem right.

UPDATE: Also, it converts HTML entities to numeric equivalents (e.g. turns &copy; into &#169;).
UPDATE: Also, why does it add the <tdbody> tag to a table, when original code didn't have it?

Muptilpe Class Selector

CSS:

.column {
    width: 300px;
    float:left;
}
.social .column {
    width: 280px;
    min-width: 279px;
    float:left;
}

HTML:

<table class="social" width="100%">
    <tr>
        <td>
        <table align="left" class="column"></table>

Expected result is for the inner table to have width:280px. Actual result is that it has a width of 300px.

Setting background URL to an image in a style will not work

You can't use images for background as setting a relative image makes no sense in an email and using the following syntax is broken by the parser:

        background: url('http://my.web.site.com/Content/email/content.png') repeat-y;

This is because the colon character is used to break the string and it ends up as:

background: url('http;margin: 0;) <-- Missed the main URL.

NullReferenceException when no style element

When the htmlInput does not contains any style element, a NullReferenceException is thrown.

It's not really explicite without the sources. and perhaps the content is dynamic and it contains no element of style

In PreMailer.cs (22):

var styleNodes = doc.DocumentNode.SelectNodes("//style");

foreach (var style in styleNodes) { ... }

Thanks a lot for your works

Bgcolor attribute

Hi

Just wrote a quick test to see if PreMailer added a bgcolor attirbute but it doesn't seem to do this. Has this ever been requested? If so was it deemed not worth supporting? bgcolor is a reliable way of styling html emails and I would have thought this has come up before.

Thanks

Richard

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.