Пример #1
0
        // https://github.com/mjmlio/mjml/blob/d4c6ea0744e05c928044108c3117c16a9c4110fe/packages/mjml-core/src/createComponent.js#L115
        public virtual CssBoxModel GetBoxModel()
        {
            // LR: Default to the outmost container
            CssParsedUnit containerWidth = CssUnitParser.Parse(HtmlSkeleton.ContainerWidth);

            var paddings =
                GetShorthandAttributeValue("padding", "right") +
                GetShorthandAttributeValue("padding", "left");

            var borders =
                GetShorthandBorderValue("right") +
                GetShorthandBorderValue("left");

            // LR: Try and get the parents calculated Box Model size.
            if (HasParentComponent())
            {
                var parent = GetParentComponent() as BodyComponent;

                // LR: Calculate based of the parents inner width (width after removing paddings and borders)
                containerWidth.Value = parent.GetContainerInnerWidth() - paddings - borders;

                return(new CssBoxModel(
                           parent.GetContainerInnerWidth(),
                           borders,
                           paddings,
                           containerWidth.Value
                           ));
            }

            return(new CssBoxModel(
                       containerWidth.Value,
                       borders,
                       paddings,
                       containerWidth.Value));
        }
Пример #2
0
        public float GetShorthandBorderValue(string direction)
        {
            string mjAttributeDirection = GetAttribute($"border-{direction}");
            string mjAttribute          = GetAttribute("border");

            if (!string.IsNullOrWhiteSpace(mjAttributeDirection))
            {
                return(CssUnitParser.Parse(mjAttributeDirection).Value);
            }

            if (string.IsNullOrWhiteSpace(mjAttribute))
            {
                return(0);
            }

            // MERGED borderParser: https://github.com/mjmlio/mjml/blob/d4c6ea0744e05c928044108c3117c16a9c4110fe/packages/mjml-core/src/helpers/shorthandParser.js#L3
            //return CssUnitParser.Parse(mjAttribute).Value;
            Regex regex = new Regex("(?:(?:^| )([0-9]+))");
            var   match = regex.Match(mjAttribute);

            if (!match.Success)
            {
                return(0);
            }

            return(float.Parse(match.Value.Trim()));
        }
Пример #3
0
        public override string RenderMjml()
        {
            var height = GetAttribute("height");

            if (string.IsNullOrWhiteSpace(height))
            {
                height = "20px";
            }

            return($@"
                {TagHelpers.ConditionalTag($@"
                    <table role=""presentation"" border=""0"" cellpadding=""0"" cellspacing=""0"">
                        <tr>
                            <td height=""{CssUnitParser.Parse(height).Value}"" style=""vertical-align:top;height:{height};"">
                ")}

                <div {HtmlAttributes(new Dictionary<string, string> {
                        { "style", "div"}
                     })}
                >
                    &nbsp;
                </div>

                {TagHelpers.ConditionalTag($@"
                            </td>
                        </tr>
                    </table>
                ")}
            ");
        }
Пример #4
0
        private string GetWidth()
        {
            string        width       = GetAttribute("width");
            CssParsedUnit parsedWidth = CssUnitParser.Parse(width);

            return(parsedWidth.Unit == "%" ? width : $"{parsedWidth.Value}px");
        }
Пример #5
0
        public string GetMobileWidth()
        {
            var width       = GetAttribute("width");
            var mobileWidth = GetAttribute("mobileWidth");

            if (string.IsNullOrWhiteSpace(mobileWidth))
            {
                return("100%");
            }

            if (string.IsNullOrWhiteSpace(width))
            {
                return($"{100 / ParentSectionColumnCount}%");
            }

            var parsedWidth = CssUnitParser.Parse(width);

            switch (parsedWidth.Unit.ToLower())
            {
            case "%":
                return(width);

            case "px":
            default:
                return($"{parsedWidth.Value / CssUnitParser.Parse(ContainerWidth).Value}%");
            }
        }
Пример #6
0
        public string GetContentWidth()
        {
            CssParsedUnit width = HasAttribute("width") ?
                                  CssUnitParser.Parse(GetAttribute("width")) :
                                  CssUnitParser.Parse($"{999999}px");

            return(width.Value < GetContainerInnerWidth() ? width.ToString() : $"{GetContainerInnerWidth()}px");
        }
Пример #7
0
        public float GetShorthandAttributeValue(string attribute, string direction)
        {
            string mjAttributeDirection = GetAttribute($"{attribute}-{direction}");
            string mjAttribute          = GetAttribute(attribute);

            if (!string.IsNullOrWhiteSpace(mjAttributeDirection))
            {
                return(CssUnitParser.Parse(mjAttributeDirection).Value);
            }

            if (string.IsNullOrWhiteSpace(mjAttribute))
            {
                return(0);
            }

            // MERGED shorthandParser: https://github.com/mjmlio/mjml/blob/d4c6ea0744e05c928044108c3117c16a9c4110fe/packages/mjml-core/src/helpers/shorthandParser.js#L3
            var splittedCssValue = mjAttribute.Split(' ');
            Dictionary <string, int> directions;

            switch (splittedCssValue.Length)
            {
            case 2:
                directions = new Dictionary <string, int>
                {
                    { "top", 0 },
                    { "bottom", 0 },
                    { "left", 1 },
                    { "right", 1 }
                };
                break;

            case 3:
                directions = new Dictionary <string, int>
                {
                    { "top", 0 },
                    { "bottom", 2 },
                    { "left", 1 },
                    { "right", 1 }
                };
                break;

            case 4:
                directions = new Dictionary <string, int>
                {
                    { "top", 0 },
                    { "bottom", 2 },
                    { "left", 3 },
                    { "right", 1 }
                };
                break;

            case 1:
            default:
                return(CssUnitParser.Parse(mjAttribute).Value);
            }

            return(CssUnitParser.Parse(splittedCssValue[directions[direction]]).Value);
        }
Пример #8
0
        public override CssBoxModel GetBoxModel()
        {
            // LR: Default to the outmost container
            CssParsedUnit containerWidth = CssUnitParser.Parse(HtmlSkeleton.ContainerWidth);

            // LR: Try and get the parents calculated Box Model size.
            if (HasParentComponent())
            {
                var parent = GetParentComponent() as BodyComponent;

                // LR: Get columns in this section
                ParentSectionColumnCount = GetSectionColumnCount();

                // LR: Parent section width
                var sectionWidth = parent.CssBoxModel.BoxWidth;

                // LR: Column has width attribute?
                if (HasAttribute("width"))
                {
                    ContainerWidth = GetAttribute("width");
                }
                else
                {
                    ContainerWidth = $"{sectionWidth / ParentSectionColumnCount}px";
                }

                // LR: Parse the calculated width
                CssParsedUnit parsedWidth = CssUnitParser.Parse(ContainerWidth);

                // LR: Handle Percentage values
                if (parsedWidth.Unit.Equals("%", StringComparison.InvariantCultureIgnoreCase))
                {
                    parsedWidth.Value = sectionWidth * parsedWidth.Value / 100;
                    ContainerWidth    = $"{parsedWidth.Value}px";
                }
                else
                {
                    ContainerWidth = $"{parsedWidth.Value}px";
                }

                // LR: Calculated column width
                var columnWidth = CssUnitParser.Parse(ContainerWidth);

                return(new CssBoxModel(
                           parsedWidth.Value,
                           0,
                           0,
                           columnWidth.Value
                           ));
            }

            return(new CssBoxModel(
                       containerWidth.Value,
                       0,
                       0,
                       containerWidth.Value));
        }
Пример #9
0
        public string GetWidthAsPixel()
        {
            var parsedWidth          = GetParsedWidth();
            var parsedContainerWidth = CssUnitParser.Parse(ContainerWidth);

            if (parsedWidth.Unit.Equals("%", StringComparison.InvariantCultureIgnoreCase))
            {
                return($"{ parsedContainerWidth.Value * parsedWidth.Value / 100}px");
            }

            return(parsedWidth.ToString());
        }
Пример #10
0
        private object RenderMode()
        {
            string background = GetAttribute("background-url");

            switch (GetAttribute("mode"))
            {
            case "fluid-height":
                string magicTd = HtmlAttributes(new Dictionary <string, string> {
                    { "style", "td-fluid" },
                });

                return($@"
                        <td {magicTd} />
                        <td {HtmlAttributes(new Dictionary<string, string> {
                                    { "background", background },
                                    { "style", "hero" }
                                })}
                        >
                           {RenderContent()}
                        </td>
                        <td {magicTd} />
                    ");

            default:
                var heightCss     = CssUnitParser.Parse(GetAttribute("height"));
                var paddingTop    = GetShorthandAttributeValue("padding", "top");
                var paddingBottom = GetShorthandAttributeValue("padding", "bottom");

                // LR: Start with the height value. e.g. 500px
                var height = heightCss.Value;

                // LR: Convert % to PX
                if (heightCss.Unit.Equals("%"))
                {
                    height = GetContainerInnerWidth() / 100 * height;
                }

                // LR: Remove the top and bottom padding from the height e.g. 500px - 24px - 24px = 452px
                height -= paddingTop + paddingBottom;

                return($@"
                        <td {HtmlAttributes(new Dictionary<string, string> {
                                { "background", background },
                                { "style", "hero" },
                                { "height", $"{height}" }
                            })}
                        >
                           {RenderContent()}
                        </td>
                    ");
            }
        }
Пример #11
0
        public CssParsedUnit GetParsedWidth()
        {
            string width = string.Empty;

            if (HasAttribute("width"))
            {
                width = GetAttribute("width");
            }
            else
            {
                width = $"{100 / GetSectionColumnCount()}%";
            }

            CssParsedUnit parsedWidth = CssUnitParser.Parse(width);

            return(parsedWidth);
        }
Пример #12
0
        public string GetElementWidth(string width)
        {
            if (string.IsNullOrWhiteSpace(width))
            {
                var   parsedContainerWidth = CssUnitParser.Parse(ContainerWidth);
                float columnWidth          = parsedContainerWidth.Value / GetSectionColumnCount();
                return($"{columnWidth}px");
            }

            var parsedWidth = CssUnitParser.Parse(width);

            if (parsedWidth.Unit.Equals("%", StringComparison.InvariantCultureIgnoreCase))
            {
                return($"{ 100 * parsedWidth.Value / GetContainerInnerWidth()}px");
            }

            return(parsedWidth.ToString());
        }
Пример #13
0
        public CssCoordinate CalculateBackgroundAxisOrigin(string axis, CssCoordinate coordinate)
        {
            bool isX = axis.Equals("x", StringComparison.InvariantCultureIgnoreCase);
            bool isBackgroundRepeat = HasAttribute("background-repeat") && GetAttribute("background-repeat").Equals("repeat", StringComparison.InvariantCultureIgnoreCase);

            string position = isX ? coordinate.X : coordinate.Y;
            string origin   = isX ? coordinate.X : coordinate.Y;

            float positionFloat;
            float originFloat;

            if (position.Contains("%"))
            {
                var percentage = CssUnitParser.Parse(position);

                if (isBackgroundRepeat)
                {
                    positionFloat = percentage.Value;
                    originFloat   = percentage.Value;
                }
                else
                {
                    float computed = (-50 + (percentage.Value * 100)) / 100;
                    positionFloat = computed;
                    originFloat   = computed;
                }
            }
            else if (isBackgroundRepeat)
            {
                positionFloat = isX ? 0.5f : 0f;
                originFloat   = isX ? 0.5f : 0f;
            }
            else
            {
                positionFloat = isX ? 0f : -0.5f;
                originFloat   = isX ? 0f : -0.5f;
            }

            return(new CssCoordinate(originFloat.ToString(), positionFloat.ToString()));
        }
Пример #14
0
        public string CalculateAWidth(string content)
        {
            if (string.IsNullOrWhiteSpace(content))
            {
                return(null);
            }

            CssParsedUnit parsedWidth = CssUnitParser.Parse(content);

            if (!parsedWidth.Unit.Equals("px"))
            {
                return(null);
            }

            var borders = CssBoxModel.BorderWidth;

            var innerPaddings =
                GetShorthandAttributeValue("inner-padding", "left") +
                GetShorthandAttributeValue("inner-padding", "right");

            return($"{parsedWidth.Value - innerPaddings - borders}px");
        }
Пример #15
0
        public virtual string GetOutlookWidth()
        {
            var containerWidth = CssUnitParser.Parse($"{GetContainerOuterWidth()}");

            var paddingSize =
                GetShorthandAttributeValue("padding", "left") +
                GetShorthandAttributeValue("padding", "right");

            var parsedWidth = CssUnitParser.Parse(GetAttribute("width"));

            switch (parsedWidth.Unit.ToLower())
            {
            case "%":
                return($"{ (containerWidth.Value * parsedWidth.Value / 100) - paddingSize}px");

            case "px":
                return(parsedWidth.ToString());

            default:
                return($"{containerWidth.Value - paddingSize}px");
            }
        }
Пример #16
0
        public override void SetupStyles()
        {
            var backgroundHeight = CssUnitParser.Parse(GetAttribute("background-height"));
            var backgroundWidth  = CssUnitParser.Parse(GetAttribute("background-width"));
            var backgroundRatio  = Math.Round(backgroundHeight.Value / backgroundWidth.Value * 100d);
            var width            = Element.HasAttribute("background-width") ? GetAttribute("background-width") : $"{GetContainerInnerWidth()}px";

            StyleLibraries.AddStyleLibrary("div", new Dictionary <string, string>()
            {
                { "margin", "0 auto" },
                { "max-width", $"{GetContainerInnerWidth()}px" }
            });

            StyleLibraries.AddStyleLibrary("table", new Dictionary <string, string>()
            {
                { "width", "100%" }
            });

            StyleLibraries.AddStyleLibrary("tr", new Dictionary <string, string>()
            {
                { "vertical-align", "top" }
            });

            StyleLibraries.AddStyleLibrary("td-fluid", new Dictionary <string, string>()
            {
                { "width", "0.01%" },
                { "padding-bottom", $"{backgroundRatio}%" },
                { "mso-padding-bottom-alt", "0" }
            });

            StyleLibraries.AddStyleLibrary("hero", new Dictionary <string, string>()
            {
                { "background", GetBackground() },
                { "background-position", GetAttribute("background-position") },
                { "background-repeat", "no-repeat" },
                { "padding", GetAttribute("padding") },
                { "padding-top", GetAttribute("padding-top") },
                { "padding-left", GetAttribute("padding-left") },
                { "padding-right", GetAttribute("padding-right") },
                { "padding-bottom", GetAttribute("padding-bottom") },
                { "vertical-align", GetAttribute("vertical-align") }
            });

            StyleLibraries.AddStyleLibrary("outlook-table", new Dictionary <string, string>()
            {
                { "width", $"{GetContainerInnerWidth()}px" }
            });

            StyleLibraries.AddStyleLibrary("outlook-td", new Dictionary <string, string>()
            {
                { "line-height", "0" },
                { "font-size", "0a" },
                { "mso-line-height-rule", "exactly" },
            });

            StyleLibraries.AddStyleLibrary("outlook-inner-table", new Dictionary <string, string>()
            {
                { "width", $"{GetContainerInnerWidth()}px" }
            });

            StyleLibraries.AddStyleLibrary("outlook-image", new Dictionary <string, string>()
            {
                { "border", "0" },
                { "height", GetAttribute("background-height") },
                { "mso-position-horizontal", "center" },
                { "position", "absolute" },
                { "top", "0" },
                { "width", width },
                { "z-index", "-3" }
            });

            StyleLibraries.AddStyleLibrary("outlook-inner-td", new Dictionary <string, string>()
            {
                { "background-color", GetAttribute("inner-background-color") },
                { "padding", GetAttribute("padding") },
                { "padding-top", GetAttribute("padding-top") },
                { "padding-left", GetAttribute("padding-left") },
                { "padding-right", GetAttribute("padding-right") },
                { "padding-bottom", GetAttribute("padding-bottom") },
            });

            StyleLibraries.AddStyleLibrary("inner-table", new Dictionary <string, string>()
            {
                { "width", "100%" },
                { "margin", "0px" }
            });

            StyleLibraries.AddStyleLibrary("inner-div", new Dictionary <string, string>()
            {
                { "background-color", GetAttribute("inner-background-color") },
                { "float", GetAttribute("align") },
                { "margin", "0px auto" },
                { "width", GetAttribute("width") }
            });
        }
Пример #17
0
        public override CssBoxModel GetBoxModel()
        {
            // LR: Default to the outmost container
            CssParsedUnit containerWidth = CssUnitParser.Parse(HtmlSkeleton.ContainerWidth);

            // LR: Get Padding
            var paddings =
                GetShorthandAttributeValue("padding", "right") +
                GetShorthandAttributeValue("padding", "left");

            // LR: Get Borders
            var borders =
                GetShorthandBorderValue("right") +
                GetShorthandBorderValue("left");

            // LR: Get inner-borders
            float innerBorders =
                GetShorthandAttributeValue("inner-border", "left") +
                GetShorthandAttributeValue("inner-border", "right");

            // LR: All padding
            float allPaddings = paddings + borders + innerBorders;

            // LR: Try and get the parents calculated Box Model size.
            if (HasParentComponent())
            {
                var parent = GetParentComponent() as BodyComponent;

                // LR: Get columns in this section
                ParentSectionColumnCount = GetSectionColumnCount();

                // LR: Parent section width
                var sectionWidth = parent.CssBoxModel.BoxWidth;

                // LR: Column has width attribute?
                if (HasAttribute("width"))
                {
                    ContainerWidth = GetAttribute("width");
                }
                else
                {
                    ContainerWidth = $"{sectionWidth / ParentSectionColumnCount}px";
                }

                // LR: Parse the calculated width
                CssParsedUnit parsedWidth = CssUnitParser.Parse(ContainerWidth);

                // LR: Handle Percentage values
                if (parsedWidth.Unit.Equals("%", StringComparison.InvariantCultureIgnoreCase))
                {
                    parsedWidth.Value = (sectionWidth * parsedWidth.Value / 100);
                    ContainerWidth    = $"{parsedWidth.Value - allPaddings}";
                }
                else
                {
                    ContainerWidth = $"{parsedWidth.Value - allPaddings}px";
                }

                // LR: Calculated column width
                var columnWidth = CssUnitParser.Parse(ContainerWidth);

                return(new CssBoxModel(
                           parsedWidth.Value,
                           borders,
                           paddings,
                           columnWidth.Value
                           ));
            }

            return(new CssBoxModel(
                       containerWidth.Value,
                       borders,
                       paddings,
                       containerWidth.Value));
        }
Пример #18
0
        public string RenderWithBackground(string content)
        {
            CssParsedUnit containerWidth = CssUnitParser.Parse($"{GetContainerOuterWidth()}");

            bool isFullWidth  = IsFullWidth();
            bool isPercentage = containerWidth.Unit.Equals("%", StringComparison.InvariantCultureIgnoreCase);

            CssCoordinate backgroundPosition = GetBackgroundPosition();

            // LR: Convert direction to Percentage X
            switch (backgroundPosition.X)
            {
            case "left":
                backgroundPosition.X = "0%";
                break;

            case "center":
                backgroundPosition.X = "50%";
                break;

            case "right":
                backgroundPosition.X = "100%";
                break;

            default:
                if (!backgroundPosition.X.ToString().Contains("%"))
                {
                    backgroundPosition.X = "50%";
                }
                break;
            }

            // LR: Convert direction to Percentage Y
            switch (backgroundPosition.Y)
            {
            case "top":
                backgroundPosition.Y = "0%";
                break;

            case "center":
                backgroundPosition.Y = "50%";
                break;

            case "bottom":
                backgroundPosition.Y = "100%";
                break;

            default:
                if (!backgroundPosition.Y.ToString().Contains("%"))
                {
                    backgroundPosition.Y = "0%";
                }
                break;
            }

            // LR: Calculate position to origin
            // X = Axis Origin, Y = Axis Position
            // This logic is different when using repeat or no-repeat
            var originX = CalculateBackgroundAxisOrigin("x", backgroundPosition);
            var originY = CalculateBackgroundAxisOrigin("y", backgroundPosition);

            Dictionary <string, string> vSizeAttributes = new Dictionary <string, string>();

            // If background size is either cover or contain, we tell VML to keep the aspect
            // and fill the entire element.
            if (GetAttribute("background-size").Equals("cover", StringComparison.InvariantCultureIgnoreCase) ||
                GetAttribute("background-size").Equals("contain", StringComparison.InvariantCultureIgnoreCase))
            {
                vSizeAttributes = new Dictionary <string, string>
                {
                    { "size", "1,1" },
                    {
                        "aspect",
                        GetAttribute("background-size").Equals("cover", StringComparison.InvariantCultureIgnoreCase) ? "atleast" : "atmost"
                    },
                };
            }
            else if (!GetAttribute("background-size").Equals("auto", StringComparison.InvariantCultureIgnoreCase))
            {
                string backgroundSize = GetAttribute("background-size");
                var    bgSplit        = backgroundSize.Split(' ');

                if (bgSplit.Length.Equals(1))
                {
                    vSizeAttributes = new Dictionary <string, string>
                    {
                        { "size", GetAttribute("background-size") },
                        { "aspect", "atmost" }, // reproduces height auto
                    };
                }
                else
                {
                    vSizeAttributes = new Dictionary <string, string>
                    {
                        { "size", backgroundSize.Replace(' ', ',') },
                    };
                }
            }

            var vmlType = GetAttribute("background-repeat").Equals("no-repeat", StringComparison.InvariantCultureIgnoreCase) ? "frame" : "tile";

            if (GetAttribute("background-size").Equals("auto", StringComparison.InvariantCultureIgnoreCase))
            {
                vmlType = "tile"; // If no size provided, keep old behavior because outlook can't use original image size with "frame"

                // Also ensure that images are still cropped the same way
                originX = new CssCoordinate("0.5", "0.5");
                originY = new CssCoordinate("0", "0");
            }

            return($@"
                <!--[if mso | IE]>
                <v:rect {HtmlAttributes(new Dictionary<string, string> {
                    { "style",
                        isFullWidth ?
                        InlineCss(new Dictionary<string, string>{ { "mso-width-percent", "1000"} }) :
                        InlineCss(new Dictionary<string, string>{ { "width", $"{GetContainerOuterWidth()}px" } })
                    },
                    { "xmlns:v", "urn:schemas-microsoft-com:vml"},
                    { "fill", "true"},
                    { "stroke", "false"},
                })}>
                    <v:fill {HtmlAttributes(new Dictionary<string, string>
                            {
                                { "origin", $"{originX.X}, {originY.X}"},
                                { "position", $"{originX.Y}, {originY.Y}" },
                                { "src", GetAttribute("background-url") },
                                { "color", GetAttribute("background-color") },
                                { "type", vmlType }
                            }.MergeLeft(vSizeAttributes))} />
                    <v:textbox style=""mso-fit-shape-to-text:true"" inset=""0,0,0,0"">
                <![endif]-->
                {content}
                <!--[if mso | IE]>
                    </v:textbox>
                </v:rect>
                <![endif]-->
            ");
        }
Пример #19
0
        public string RenderImage()
        {
            bool   bHasHeight = HasAttribute("height");
            string height     = GetAttribute("height");

            string img = $@"
                <img {HtmlAttributes(new Dictionary<string, string> {
                        { "alt", GetAttribute("alt") },
                        { "height", bHasHeight && height.Equals("auto", StringComparison.InvariantCultureIgnoreCase) ? height : CssUnitParser.Parse(height).Value.ToString() },
                        { "src", GetAttribute("src") },
                        { "srcset", GetAttribute("srcset") },
                        { "sizes", GetAttribute("sizes") },
                        { "style", "img" },
                        { "title", GetAttribute("title") },
                        { "width", GetContentWidth() },
                        { "usemap", GetAttribute("usemap") },
                     })}
                />
            ";

            if (HasAttribute("href"))
            {
                return($@"
                    <a {HtmlAttributes(new Dictionary<string, string>() {
                            { "href", GetAttribute("href") },
                            { "target", GetAttribute("target") },
                            { "rel", GetAttribute("rel") },
                            { "name", GetAttribute("name") },
                        })}
                    >
                    {img}
                    </a>
                ");
            }

            return(img);
        }