If I have a buyer without a FirstName, Address2, shipment, zipcode, StateProvince, etc. and only buys a product for download, below standard template creates an email looking not to nice.

------------------------------------------------------------------------------------
Template:

Billing Address
%Order.BillingFirstName% %Order.BillingLastName%
%Order.BillingAddress1%
%Order.BillingAddress2%
%Order.BillingZipPostalCode% %Order.BillingCity%
%Order.BillingStateProvince% %Order.BillingCountry%

Shipping Address
%Order.ShippingFirstName% %Order.ShippingLastName%
%Order.ShippingAddress1%
%Order.ShippingAddress2%
%Order.ShippingCity% %Order.ShippingZipPostalCode%
%Order.ShippingStateProvince% %Order.ShippingCountry%

Shipping Method: %Order.ShippingMethod%

Email:

LastName
Address1

  City
   Country

Shipping Address





Shipping Method:

------------------------------------------------------------------------------------

With below modifications it will look much better..

------------------------------------------------------------------------------------
Template:

Billing Address
[trim]%Order.BillingFirstName% %Order.BillingLastName%
%Order.BillingAddress1%
%Order.BillingAddress2%
%Order.BillingZipPostalCode% %Order.BillingCity%
%Order.BillingStateProvince% %Order.BillingCountry%[/trim]

[trim]Shipping Address[-trim]
%Order.ShippingFirstName% %Order.ShippingLastName%
%Order.ShippingAddress1%
%Order.ShippingAddress2%
%Order.ShippingCity% %Order.ShippingZipPostalCode%
%Order.ShippingStateProvince% %Order.ShippingCountry%[/trim]

[trim]Shipping Method: [-trim]%Order.ShippingMethod%[/trim]


Email:

Billing Address
LastName
Address1
City
Country


or an email with shipment:

Billing Address
LastName
Address1
City
Country

Shipment Address
LastName
Address1
City
Country

Shipping Method: By Air


------------------------------------------------------------------------------------

In ..\Nop.BusinessLogic\Messages\MessageManager.cs
Add this method:
 
        //MOD_TRIMTEMPLATE>
        public static string TrimSpacesAndLineFeeds(string sValue)
        {
            string sTagStart = "[trim]";
            string sTagEnd = "[/trim]";
            int iPositionStart = 0; //Start point
            int iPositionEnd = 0;
            bool bFound = true;

            while (bFound)
            {
                bFound = false;
                iPositionEnd = -1;
                iPositionStart = sValue.IndexOf(sTagStart, iPositionStart, StringComparison.Ordinal);

                if (iPositionStart > -1)
                {
                    iPositionEnd = sValue.IndexOf(sTagEnd, iPositionStart, StringComparison.Ordinal);
                }

                //start and end found
                if ((iPositionEnd > iPositionStart) && (iPositionStart > -1))
                {
                    bFound = true;
                    bool bBreaksFound = false;
                    bool bEncodedBreaksFound = false;
                    bool bNBSPFound = false;
                    string sBetweenTags = string.Empty;

                    if (iPositionEnd - iPositionStart > 0)
                    {
                        sBetweenTags = sValue.Substring(iPositionStart + sTagStart.Length, iPositionEnd - iPositionStart - sTagStart.Length);
                    }

                    //Is this the correct end tag
                    if ((sBetweenTags.Length > 0) && (iPositionStart < sValue.Length))
                    {
                        int iPosAnotherOpenTag = sBetweenTags.IndexOf(sTagStart, 0, StringComparison.Ordinal);
                        if (iPosAnotherOpenTag > -1)
                        {
                            iPositionStart += 1; //new start point
                            continue;
                        }
                    }
                    //Remove between start and end tag
                    sValue = sValue.Remove(iPositionStart, sTagStart.Length + sBetweenTags.Length + sTagEnd.Length);
                    if (sBetweenTags.Length > 0)
                    {
                        //Remove crlfs and spaces
                        string sWhatsLeft = string.Empty;

                        if (sBetweenTags.IndexOf("&lt;br /&gt;", 0, StringComparison.Ordinal) > -1)
                        {
                            bEncodedBreaksFound = true;
                        }
                        if (sBetweenTags.IndexOf("<br />", 0, StringComparison.Ordinal) > -1)
                        {
                            bBreaksFound = true;
                        }
                        if (sBetweenTags.IndexOf("&nbsp;", 0, StringComparison.Ordinal) > -1)
                        {
                            bNBSPFound = true;
                        }
                        sWhatsLeft = sBetweenTags;
                        sWhatsLeft = sWhatsLeft.Replace(" ", string.Empty);
                        sWhatsLeft = System.Text.RegularExpressions.Regex.Replace(sWhatsLeft, @"<(.|\n)*?>", string.Empty); //Remove any between < >
                        sWhatsLeft = System.Text.RegularExpressions.Regex.Replace(sWhatsLeft, @"&lt;(.|\n)*?&gt;", string.Empty); //Remove any between < >
                        sWhatsLeft = System.Text.RegularExpressions.Regex.Replace(sWhatsLeft, @"&(.|\n)*?;", string.Empty); //Remove any between & ;
                        sWhatsLeft = sWhatsLeft.Replace("\r", "").Replace("\n", string.Empty);
                        if (sWhatsLeft.Length == 0)
                        {
                            sBetweenTags = string.Empty;
                        }
                        if (sBetweenTags.Length > 0)
                        {
                            sBetweenTags = sBetweenTags.Replace("&nbsp;", " ");

                            //Remove double spaces
                            while (sBetweenTags.IndexOf("  ") > -1)
                            {
                                sBetweenTags = sBetweenTags.Replace("  ", " "); ;
                            }

                            //Convert to /r/n
                            sBetweenTags = sBetweenTags.Replace("&lt;br /&gt;", "\r\n");
                            sBetweenTags = sBetweenTags.Replace("<br />", "\r\n");

                            //Remove single space on line
                            while (sBetweenTags.IndexOf(" \r\n") > -1)
                            {
                                sBetweenTags = sBetweenTags.Replace(" \r\n", "\r\n"); ;
                            }
                            while (sBetweenTags.IndexOf("\r\n ") > -1)
                            {
                                sBetweenTags = sBetweenTags.Replace("\r\n ", "\r\n"); ;
                            }
                            //Remove double crlfs
                            while (sBetweenTags.IndexOf("\r\n\r\n") > -1)
                            {
                                sBetweenTags = sBetweenTags.Replace("\r\n\r\n", "\r\n");
                            }
                            sBetweenTags = sBetweenTags.Trim(); //Remove space Before and After

                            sBetweenTags = sBetweenTags.TrimStart().TrimEnd();
                            if (sBetweenTags.EndsWith("[-trim]", StringComparison.Ordinal))
                            {
                                sBetweenTags = string.Empty;

                                //Cutoff crlf after tags too?
                                if (iPositionStart + 1 < sValue.Length)
                                {
                                    string sAfterTags = sValue.Substring(iPositionStart);
                                    if (sAfterTags.IndexOf("\r\n") == 0)
                                    {
                                        sValue = sValue.Remove(iPositionStart, ("\r\n").Length);
                                    }
                                    else if (sAfterTags.IndexOf("&lt;br /&gt;") == 0)
                                    {
                                        sValue = sValue.Remove(iPositionStart, ("&lt;br /&gt;").Length);
                                    }
                                    else if (sAfterTags.IndexOf("<br />") == 0)
                                    {
                                        sValue = sValue.Remove(iPositionStart, ("<br />").Length);
                                    }

                                }
                            }

                            //Convert /r/n to <br />
                            if (bEncodedBreaksFound)
                            {
                                sBetweenTags = sBetweenTags.Replace("\r\n", "&lt;br /&gt;");
                            }
                            if (bBreaksFound)
                            {
                                sBetweenTags = sBetweenTags.Replace("\r\n", "<br />");
                            }
                            if (bNBSPFound)
                            {
                                sBetweenTags = sBetweenTags.Replace(" ", "&nbsp;");
                            }


                            //Insert trimmed text
                            if (iPositionStart < sValue.Length)
                            {
                                //If we have trimmed of all between the tags then cutoff first crlf too
                                sValue = sValue.Insert(iPositionStart, sBetweenTags);
                            }
                            else
                            {
                                sValue = sValue + sBetweenTags;
                            }

                        }

                    }
                    //iPositionStart = sValue.re.IndexOf(sTagStart, iPositionStart, StringComparison.Ordinal);

                    iPositionStart = 0; //new start point //restart
                }
            }

            sValue = sValue.Replace("[-trim]", ""); //Remove leftovers
            return sValue;
        }
        //MOD_TRIMTEMPLATE<


2.
Replace the end of every function "ReplaceMessageTemplateTokens":
            
return template;


with:
            
//MOD_TRIMTEMPLATE>
return TrimSpacesAndLineFeeds(template);
//MOD_TRIMTEMPLATE<