/// <summary>
        /// Load stylesheet data from the given source.<br/>
        /// The source can be local file or web URI.<br/>
        /// First raise <see cref="HtmlStylesheetLoadEventArgs"/> event to allow the client to overwrite the stylesheet loading.<br/>
        /// If the stylesheet is downloaded from URI we will try to correct local URIs to absolute.<br/>
        /// </summary>
        /// <param name="htmlContainer">the container of the html to handle load stylesheet for</param>
        /// <param name="src">the source of the element to load the stylesheet by</param>
        /// <param name="attributes">the attributes of the link element</param>
        /// <param name="stylesheet">return the stylesheet string that has been loaded (null if failed or <paramref name="stylesheetData"/> is given)</param>
        /// <param name="stylesheetData">return stylesheet data object that was provided by overwrite (null if failed or <paramref name="stylesheet"/> is given)</param>
        public static void LoadStylesheet(HtmlContainerInt htmlContainer, string src, Dictionary<string, string> attributes, out string stylesheet, out CssData stylesheetData)
        {
            ArgChecker.AssertArgNotNull(htmlContainer, "htmlContainer");

            stylesheet = null;
            stylesheetData = null;
            try
            {
                var args = new HtmlStylesheetLoadEventArgs(src, attributes);
                htmlContainer.RaiseHtmlStylesheetLoadEvent(args);

                if (!string.IsNullOrEmpty(args.SetStyleSheet))
                {
                    stylesheet = args.SetStyleSheet;
                }
                else if (args.SetStyleSheetData != null)
                {
                    stylesheetData = args.SetStyleSheetData;
                }
                else if (args.SetSrc != null)
                {
                    stylesheet = LoadStylesheet(htmlContainer, args.SetSrc);
                }
                else
                {
                    stylesheet = LoadStylesheet(htmlContainer, src);
                }
            }
            catch (Exception ex)
            {
                htmlContainer.ReportError(HtmlRenderErrorType.CssParsing, "Exception in handling stylesheet source", ex);
            }
        }
예제 #2
0
        /// <summary>
        /// Generate css tree by parsing the given html and applying the given css style data on it.
        /// </summary>
        /// <param name="html">the html to parse</param>
        /// <param name="htmlContainer">the html container to use for reference resolve</param>
        /// <param name="cssData">the css data to use</param>
        /// <returns>the root of the generated tree</returns>
        public CssBox GenerateCssTree(string html, HtmlContainerInt htmlContainer, ref CssData cssData)
        {
            var root = HtmlParser.ParseDocument(html);
            if (root != null)
            {
                root.HtmlContainer = htmlContainer;

                bool cssDataChanged = false;
                CascadeParseStyles(root, htmlContainer, ref cssData, ref cssDataChanged);

                CascadeApplyStyles(root, cssData);

                SetTextSelectionStyle(htmlContainer, cssData);

                CorrectTextBoxes(root);

                CorrectImgBoxes(root);

                bool followingBlock = true;
                CorrectLineBreaksBlocks(root, ref followingBlock);

                CorrectInlineBoxesParent(root);

                CorrectBlockInsideInline(root);

                CorrectInlineBoxesParent(root);
            }
            return root;
        }
예제 #3
0
 /// <summary>
 /// Asigns the given css style blocks to the given css box checking if matching.
 /// </summary>
 /// <param name="box">the css box to assign css to</param>
 /// <param name="cssData">the css data to use to get the matching css blocks</param>
 /// <param name="className">the class selector to search for css blocks</param>
 private static void AssignCssBlocks(CssBox box, CssData cssData, string className)
 {
     var blocks = cssData.GetCssBlock(className);
     foreach (var block in blocks)
     {
         if (IsBlockAssignableToBox(box, block))
         {
             AssignCssBlock(box, block);
         }
     }
 }
예제 #4
0
        /// <summary>
        /// Generate css tree by parsing the given html and applying the given css style data on it.
        /// </summary>
        /// <param name="html">the html to parse</param>
        /// <param name="cssData">the css data to use</param>
        /// <param name="bridge">used to resolve external style references in html code</param>
        /// <returns>the root of the generated tree</returns>
        public static CssBox GenerateCssTree(string html, ref CssData cssData, object bridge)
        {
            var root = HtmlParser.ParseDocument(html);
            if (root != null)
            {
                bool cssDataChanged = false;
                CascadeStyles(root, bridge, ref cssData, ref cssDataChanged);

                CorrectLineBreaksBlocks(root);

                CorrectBlockInsideInline(root);

                CorrectInlineBoxesParent(root);
            }
            return root;
        }
예제 #5
0
 public override void SetOn(CssData data)
 {
     data.AddAttribute(Selector, "font-variant", "small-caps");
 }
예제 #6
0
        /// <summary>
        /// Parse given stylesheet for media CSS blocks<br/>
        /// This blocks are added under the specific media block they are found.
        /// </summary>
        /// <param name="cssData">the CSS data to fill with parsed CSS objects</param>
        /// <param name="stylesheet">the stylesheet to parse</param>
        private void ParseMediaStyleBlocks(CssData cssData, string stylesheet)
        {
            int startIdx = 0;
            string atrule;
            while ((atrule = RegexParserUtils.GetCssAtRules(stylesheet, ref startIdx)) != null)
            {
                //Just process @media rules
                if (!atrule.StartsWith("@media", StringComparison.OrdinalIgnoreCase))
                    continue;

                //Extract specified media types
                MatchCollection types = RegexParserUtils.Match(RegexParserUtils.CssMediaTypes, atrule);

                if (types.Count == 1)
                {
                    string line = types[0].Value;

                    if (line.StartsWith("@media", StringComparison.OrdinalIgnoreCase) && line.EndsWith("{"))
                    {
                        //Get specified media types in the at-rule
                        string[] media = line.Substring(6, line.Length - 7).Split(' ');

                        //Scan media types
                        foreach (string t in media)
                        {
                            if (!String.IsNullOrEmpty(t.Trim()))
                            {
                                //Get blocks inside the at-rule
                                var insideBlocks = RegexParserUtils.Match(RegexParserUtils.CssBlocks, atrule);

                                //Scan blocks and feed them to the style sheet
                                foreach (Match insideBlock in insideBlocks)
                                {
                                    FeedStyleBlock(cssData, insideBlock.Value, t.Trim());
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Parse the given stylesheet source to CSS blocks dictionary.<br/>
        /// The CSS blocks are organized into two level buckets of media type and class name.<br/>
        /// Root media type are found under 'all' bucket.<br/>
        /// The parsed css blocks are added to the given css data, merged if class name already exists.
        /// </summary>
        /// <param name="cssData">the CSS data to fill with parsed CSS objects</param>
        /// <param name="stylesheet">raw css stylesheet to parse</param>
        public void ParseStyleSheet(CssData cssData, string stylesheet)
        {
            if (!String.IsNullOrEmpty(stylesheet))
            {
                stylesheet = RemoveStylesheetComments(stylesheet);

                ParseStyleBlocks(cssData, stylesheet);

                ParseMediaStyleBlocks(cssData, stylesheet);
            }
        }
예제 #8
0
        /// <summary>
        /// Parse the given stylesheet source to CSS blocks dictionary.<br/>
        /// The CSS blocks are organized into two level buckets of media type and class name.<br/>
        /// Root media type are found under 'all' bucket.<br/>
        /// The parsed css blocks are added to the given css data, merged if class name already exists.
        /// </summary>
        /// <param name="cssData">the CSS data to fill with parsed CSS objects</param>
        /// <param name="stylesheet">raw css stylesheet to parse</param>
        public static void ParseStyleSheet(CssData cssData, string stylesheet)
        {
            if (!String.IsNullOrEmpty(stylesheet))
            {
                // Convert everything to lower-case so not to handle case in code
                stylesheet = stylesheet.ToLower();

                stylesheet = RemoveStylesheetComments(stylesheet);

                ParseStyleBlocks(cssData, stylesheet);

                ParseMediaStyleBlocks(cssData, stylesheet);
            }
        }
예제 #9
0
        static void Register(//PageFrameCompiler page, 
			Type cssType)
        {
            CssData data;
            if (!styles.TryGetValue(cssType, out data)) {
                data = new CssData();
                styles[cssType] = data;
            }
        }
예제 #10
0
 public override void SetOff(CssData data)
 {
 }
예제 #11
0
        /// <summary>
        /// Assigns the given css classes to the given css box checking if matching.<br/>
        /// Support multiple classes in single attribute separated by whitespace.
        /// </summary>
        /// <param name="box">the css box to assign css to</param>
        /// <param name="cssData">the css data to use to get the matching css blocks</param>
        private static void AssignClassCssBlocks(CssBox box, CssData cssData)
        {
            var classes = box.HtmlTag.TryGetAttribute("class");

            var startIdx = 0;
            while (startIdx < classes.Length)
            {
                while (startIdx < classes.Length && classes[startIdx] == ' ')
                    startIdx++;

                if (startIdx < classes.Length)
                {
                    var endIdx = classes.IndexOf(' ', startIdx);

                    if (endIdx < 0)
                        endIdx = classes.Length;

                    var cls = "." + classes.Substring(startIdx, endIdx - startIdx);
                    AssignCssBlocks(box, cssData, cls);
                    AssignCssBlocks(box, cssData, box.HtmlTag.Name + cls);

                    startIdx = endIdx + 1;
                }
            }
        }
예제 #12
0
 public override void SetOff(CssData data)
 {
     data.AddAttribute(Selector, "font-variant", "normal");
 }
예제 #13
0
        /// <summary>
        /// Create PDF pages from given HTML and appends them to the provided PDF document.<br/>
        /// </summary>
        /// <param name="document">PDF document to append pages to</param>
        /// <param name="html">HTML source to create PDF from</param>
        /// <param name="config">the configuration to use for the PDF generation (page size/page orientation/margins/etc.)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static void AddPdfPages(PdfDocument document, string html, PdfGenerateConfig config, CssData cssData = null, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            XSize orgPageSize;

            // get the size of each page to layout the HTML in
            if (config.PageSize != PageSize.Undefined)
            {
                orgPageSize = PageSizeConverter.ToSize(config.PageSize);
            }
            else
            {
                orgPageSize = config.ManualPageSize;
            }

            if (config.PageOrientation == PageOrientation.Landscape)
            {
                // invert pagesize for landscape
                orgPageSize = new XSize(orgPageSize.Height, orgPageSize.Width);
            }

            var pageSize = new XSize(orgPageSize.Width - config.MarginLeft - config.MarginRight, orgPageSize.Height - config.MarginTop - config.MarginBottom);

            if (!string.IsNullOrEmpty(html))
            {
                using (var container = new HtmlContainer())
                {
                    if (stylesheetLoad != null)
                    {
                        container.StylesheetLoad += stylesheetLoad;
                    }
                    if (imageLoad != null)
                    {
                        container.ImageLoad += imageLoad;
                    }

                    container.Location = new XPoint(config.MarginLeft, config.MarginTop);
                    container.MaxSize  = new XSize(pageSize.Width, 0);
                    container.SetHtml(html, cssData);
                    container.PageSize     = pageSize;
                    container.MarginBottom = config.MarginBottom;
                    container.MarginLeft   = config.MarginLeft;
                    container.MarginRight  = config.MarginRight;
                    container.MarginTop    = config.MarginTop;

                    // layout the HTML with the page width restriction to know how many pages are required
                    using (var measure = XGraphics.CreateMeasureContext(pageSize, XGraphicsUnit.Point, XPageDirection.Downwards))
                    {
                        container.PerformLayout(measure);
                    }

                    // while there is un-rendered HTML, create another PDF page and render with proper offset for the next page
                    double scrollOffset = 0;
                    while (scrollOffset > -container.ActualSize.Height)
                    {
                        var page = document.AddPage();
                        page.Height = orgPageSize.Height;
                        page.Width  = orgPageSize.Width;

                        using (var g = XGraphics.FromPdfPage(page))
                        {
                            //g.IntersectClip(new XRect(config.MarginLeft, config.MarginTop, pageSize.Width, pageSize.Height));
                            g.IntersectClip(new XRect(0, 0, page.Width, page.Height));

                            container.ScrollOffset = new XPoint(0, scrollOffset);
                            container.PerformPaint(g);
                        }
                        scrollOffset -= pageSize.Height;
                    }

                    // add web links and anchors
                    HandleLinks(document, container, orgPageSize, pageSize);
                }
            }
        }
 /// <summary>
 /// Init with optional document and stylesheet.
 /// </summary>
 /// <param name="htmlSource">the html to init with, init empty if not given</param>
 /// <param name="baseCssData">optional: the stylesheet to init with, init default if not given</param>
 public void SetHtml(string htmlSource, CssData baseCssData = null)
 {
     _htmlContainerInt.SetHtml(htmlSource, baseCssData);
 }
예제 #15
0
 /// <summary>
 /// Only used as a commodity during testing.
 /// </summary>
 public override void InsertCss(CssData cssData)
 {
     InsertCss(new CssPropertiesSet(), cssData);
 }
예제 #16
0
        /// <summary>
        /// Create PDF pages from given HTML and appends them to the provided PDF document.<br/>
        /// </summary>
        /// <param name="document">PDF document to append pages to</param>
        /// <param name="html">HTML source to create PDF from</param>
        /// <param name="config">the configuration to use for the PDF generation (page size/page orientation/margins/etc.)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static void AddPdfPages(PdfDocument document, string html, PdfGenerateConfig config, CssData cssData = null, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            XSize orgPageSize;

            // get the size of each page to layout the HTML in
            if (config.PageSize != PageSize.Undefined)
            {
                orgPageSize = PageSizeConverter.ToSize(config.PageSize);
            }
            else
            {
                orgPageSize = config.ManualPageSize;
            }

            if (config.PageOrientation == PageOrientation.Landscape)
            {
                // invert pagesize for landscape
                orgPageSize = new XSize(orgPageSize.Height, orgPageSize.Width);
            }

            var pageSize = new XSize(orgPageSize.Width - config.MarginLeft - config.MarginRight, orgPageSize.Height - config.MarginTop - config.MarginBottom);

            if (!string.IsNullOrEmpty(html))
            {
                using (var container = new HtmlContainer())
                {
                    if (stylesheetLoad != null)
                    {
                        container.StylesheetLoad += stylesheetLoad;
                    }
                    if (imageLoad != null)
                    {
                        container.ImageLoad += imageLoad;
                    }

                    container.Location = new XPoint(config.MarginLeft, config.MarginTop);
                    container.MaxSize  = new XSize(pageSize.Width, 0);
                    container.SetHtml(html, cssData);
                    container.PageSize     = pageSize;
                    container.MarginBottom = config.MarginBottom;
                    container.MarginLeft   = config.MarginLeft;
                    container.MarginRight  = config.MarginRight;
                    container.MarginTop    = config.MarginTop;

                    // layout the HTML with the page width restriction to know how many pages are required
                    var measure = XGraphics.CreateMeasureContext(pageSize, XGraphicsUnit.Point, XPageDirection.Downwards);
                    container.PerformLayout(measure);

                    // while there is un-rendered HTML, create another PDF page and render with proper offset for the next page
                    double scrollOffset = 0;
                    //SL: if there is more than one page, increase the bottom margin to allow space for the page number
                    container.MarginBottom += scrollOffset > -container.ActualSize.Height ?  20 : 0;
                    container.PerformLayout(measure); //SL: This still does not increase the margin for the first page of a multi page.. welp
                    while (scrollOffset > -container.ActualSize.Height)
                    {
                        var page = document.AddPage();
                        page.Height = orgPageSize.Height;
                        page.Width  = orgPageSize.Width;

                        using (var g = XGraphics.FromPdfPage(page))
                        {
                            //g.IntersectClip(new XRect(config.MarginLeft, config.MarginTop, pageSize.Width, pageSize.Height));
                            g.IntersectClip(new XRect(0, 0, page.Width, page.Height));

                            container.ScrollOffset = new XPoint(0, scrollOffset);
                            container.PerformPaint(g);
                        }
                        scrollOffset -= pageSize.Height;
                    }

                    if (config.AddPageCountFoooter && document.PageCount > 1)  //Only add page numbers if more than one page
                    {
                        AddPageCountFoooter(document, pageSize);
                    }

                    // SL: Set config option to handle links or not as it crashes for
                    // some valid html links.
                    // TODO: Investigate reason for crashing.
                    if (config.HandleLinks)
                    {
                        HandleLinks(document, container, orgPageSize, pageSize);
                    }
                }
            }
        }
예제 #17
0
        private static void AddGridCols(CssData expected, params int[] gridCols)
        {
            var val = string.Join(' ', gridCols.Select(x => Style.Properties.Utils.TwipsToCm(x) + "cm"));

            expected.AddAttribute(".demo", "grid-template-columns", val);
        }
예제 #18
0
 public void InsertCss(CssPropertiesSet other, CssData cssData)
 {
     cssData.AddRange(_cssData);
 }
예제 #19
0
        /// <summary>
        /// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.<br/>
        /// If <paramref name="maxSize.Width"/> is zero the html will use all the required width, otherwise it will perform line
        /// wrap as specified in the html<br/>
        /// If <paramref name="maxSize.Height"/> is zero the html will use all the required height, otherwise it will clip at the
        /// given max height not rendering the html below it.<br/>
        /// If <paramref name="minSize"/> (Width/Height) is above zero the rendered image will not be smaller than the given min size.<br/>
        /// The generated image have transparent background that the html is rendered on.<br/>
        /// GDI+ text rending can be controlled by providing <see cref="TextRenderingHint"/>.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </summary>
        /// <param name="html">HTML source to render</param>
        /// <param name="minSize">optional: the min size of the rendered html (zero - not limit the width/height)</param>
        /// <param name="maxSize">optional: the max size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height)</param>
        /// <param name="textRenderingHint">optional: (default - SingleBitPerPixelGridFit)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static Image RenderToImageGdiPlus(string html, Size minSize, Size maxSize, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null,
                                                 EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad           = null)
        {
            if (string.IsNullOrEmpty(html))
            {
                return(new Bitmap(0, 0, PixelFormat.Format32bppArgb));
            }

            using (var container = new HtmlContainer())
            {
                container.AvoidAsyncImagesLoading = true;
                container.AvoidImagesLateLoading  = true;
                container.UseGdiPlusTextRendering = true;

                if (stylesheetLoad != null)
                {
                    container.StylesheetLoad += stylesheetLoad;
                }
                if (imageLoad != null)
                {
                    container.ImageLoad += imageLoad;
                }
                container.SetHtml(html, cssData);

                var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize);
                container.MaxSize = finalSize;

                // create the final image to render into by measured size
                var image = new Bitmap(finalSize.Width, finalSize.Height, PixelFormat.Format32bppArgb);

                // render HTML into the image
                using (var g = Graphics.FromImage(image))
                {
                    g.TextRenderingHint = textRenderingHint;
                    container.PerformPaint(g);
                }

                return(image);
            }
        }
예제 #20
0
        /// <summary>
        /// Applies style to all boxes in the tree.<br/>
        /// If the html tag has style defined for each apply that style to the css box of the tag.<br/>
        /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/>
        /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/>
        /// </summary>
        /// <param name="box">the box to apply the style to</param>
        /// <param name="cssData">the style data for the html</param>
        private void CascadeApplyStyles(CssBox box, CssData cssData)
        {
            box.InheritStyle();

            if (box.HtmlTag != null)
            {
                // try assign style using all wildcard
                AssignCssBlocks(box, cssData, "*");

                // try assign style using the html element tag
                AssignCssBlocks(box, cssData, box.HtmlTag.Name);

                // try assign style using the "class" attribute of the html element
                if (box.HtmlTag.HasAttribute("class"))
                {
                    AssignClassCssBlocks(box, cssData);
                }

                // try assign style using the "id" attribute of the html element
                if (box.HtmlTag.HasAttribute("id"))
                {
                    var id = box.HtmlTag.TryGetAttribute("id");
                    AssignCssBlocks(box, cssData, "#" + id);
                }

                TranslateAttributes(box.HtmlTag, box);

                // Check for the style="" attribute
                if (box.HtmlTag.HasAttribute("style"))
                {
                    var block = _cssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style"));
                    if (block != null)
                        AssignCssBlock(box, block);
                }
            }

            // cascade text decoration only to boxes that actually have text so it will be handled correctly.
            if (box.TextDecoration != String.Empty && box.Text == null)
            {
                foreach (var childBox in box.Boxes)
                    childBox.TextDecoration = box.TextDecoration;
                box.TextDecoration = string.Empty;
            }

            foreach (var childBox in box.Boxes)
            {
                CascadeApplyStyles(childBox, cssData);
            }
        }
예제 #21
0
        /// <summary>
        /// Renders the specified HTML source on the specified location and max size restriction.<br/>
        /// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
        /// wrap as specified in the html<br/>
        /// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
        /// given max height not rendering the html below it.<br/>
        /// Returned is the actual width and height of the rendered html.<br/>
        /// </summary>
        /// <param name="g">Device to render with</param>
        /// <param name="html">HTML source to render</param>
        /// <param name="location">the top-left most location to start render the html at</param>
        /// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="useGdiPlusTextRendering">true - use GDI+ text rendering, false - use GDI text rendering</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the actual size of the rendered html</returns>
        private static SizeF RenderHtml(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler <HtmlImageLoadEventArgs> imageLoad)
        {
            SizeF actualSize = SizeF.Empty;

            if (!string.IsNullOrEmpty(html))
            {
                using (var container = new HtmlContainer())
                {
                    container.Location = location;
                    container.MaxSize  = maxSize;
                    container.AvoidAsyncImagesLoading = true;
                    container.AvoidImagesLateLoading  = true;
                    container.UseGdiPlusTextRendering = useGdiPlusTextRendering;

                    if (stylesheetLoad != null)
                    {
                        container.StylesheetLoad += stylesheetLoad;
                    }
                    if (imageLoad != null)
                    {
                        container.ImageLoad += imageLoad;
                    }

                    container.SetHtml(html, cssData);
                    container.PerformLayout(g);
                    container.PerformPaint(g);

                    actualSize = container.ActualSize;
                }
            }

            return(actualSize);
        }
예제 #22
0
        /// <summary>
        /// Create PDF document from given HTML.<br/>
        /// </summary>
        /// <param name="html">HTML source to create PDF from</param>
        /// <param name="pageSize">the page size to use for each page in the generated pdf </param>
        /// <param name="margin">the margin to use between the HTML and the edges of each page</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static PdfDocument GeneratePdf(string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            var config = new PdfGenerateConfig();

            config.PageSize = pageSize;
            config.SetMargins(margin);
            return(GeneratePdf(html, config, cssData, stylesheetLoad, imageLoad));
        }
예제 #23
0
 public override void SetOn(CssData data)
 {
     data.AddAttribute(Selector, "display", "none!important");
 }
예제 #24
0
 /// <summary>
 /// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.<br/>
 /// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
 /// wrap as specified in the html<br/>
 /// If <paramref name="maxHeight"/> is zero the html will use all the required height, otherwise it will clip at the
 /// given max height not rendering the html below it.<br/>
 /// The generated image have transparent background that the html is rendered on.<br/>
 /// GDI+ text rending can be controlled by providing <see cref="TextRenderingHint"/>.<br/>
 /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
 /// </summary>
 /// <param name="html">HTML source to render</param>
 /// <param name="maxWidth">optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
 /// <param name="maxHeight">optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
 /// <param name="textRenderingHint">optional: (default - SingleBitPerPixelGridFit)</param>
 /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
 /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
 /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
 /// <returns>the generated image of the html</returns>
 public static Image RenderToImageGdiPlus(string html, int maxWidth = 0, int maxHeight = 0, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null,
                                          EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
 {
     return(RenderToImageGdiPlus(html, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData, stylesheetLoad, imageLoad));
 }
예제 #25
0
        /// <summary>
        /// Create PDF pages from given HTML and appends them to the provided PDF document.<br/>
        /// </summary>
        /// <param name="document">PDF document to append pages to</param>
        /// <param name="html">HTML source to create PDF from</param>
        /// <param name="pageSize">the page size to use for each page in the generated pdf </param>
        /// <param name="margin">the margin to use between the HTML and the edges of each page</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static void AddPdfPages(PdfDocument document, string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            var config = new PdfGenerateConfig();

            config.PageSize = pageSize;
            config.SetMargins(margin);
            AddPdfPages(document, html, config, cssData, stylesheetLoad, imageLoad);
        }
예제 #26
0
        /// <summary>
        /// Renders the specified HTML source on the specified location and max size restriction.<br/>
        /// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
        /// wrap as specified in the html<br/>
        /// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
        /// given max height not rendering the html below it.<br/>
        /// Clip the graphics so the html will not be rendered outside the max height bound given.<br/>
        /// Returned is the actual width and height of the rendered html.<br/>
        /// </summary>
        /// <param name="g">Device to render with</param>
        /// <param name="html">HTML source to render</param>
        /// <param name="location">the top-left most location to start render the html at</param>
        /// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="useGdiPlusTextRendering">true - use GDI+ text rendering, false - use GDI text rendering</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the actual size of the rendered html</returns>
        private static SizeF RenderClip(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler <HtmlImageLoadEventArgs> imageLoad)
        {
            Region prevClip = null;

            if (maxSize.Height > 0)
            {
                prevClip = g.Clip;
                g.SetClip(new RectangleF(location, maxSize));
            }

            var actualSize = RenderHtml(g, html, location, maxSize, cssData, useGdiPlusTextRendering, stylesheetLoad, imageLoad);

            if (prevClip != null)
            {
                g.SetClip(prevClip, CombineMode.Replace);
            }

            return(actualSize);
        }
예제 #27
0
        public static Metafile RenderToMetafile(string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null,
                                                EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            Metafile image;
            IntPtr   dib;
            var      memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, 1, 1, out dib);

            try
            {
                image = new Metafile(memoryHdc, EmfType.EmfPlusDual, "..");

                using (var g = Graphics.FromImage(image))
                {
                    Render(g, html, left, top, maxWidth, cssData, stylesheetLoad, imageLoad);
                }
            }
            finally
            {
                Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
            }
            return(image);
        }
        /// <summary>
        /// LoadFromRaw stylesheet data from the given source.<br/>
        /// The source can be local file or web URI.<br/>
        /// First raise <see cref="HtmlStylesheetLoadEventArgs"/> event to allow the client to overwrite the stylesheet loading.<br/>
        /// If the stylesheet is downloaded from URI we will try to correct local URIs to absolute.<br/>
        /// </summary>
        /// <param name="htmlContainer">the container of the html to handle load stylesheet for</param>
        /// <param name="src">the source of the element to load the stylesheet by</param>
        /// <param name="attributes">the attributes of the link element</param>
        /// <param name="stylesheet">return the stylesheet string that has been loaded (null if failed or <paramref name="stylesheetData"/> is given)</param>
        /// <param name="stylesheetData">return stylesheet data object that was provided by overwrite (null if failed or <paramref name="stylesheet"/> is given)</param>
        public static void LoadStylesheet(HtmlContainerInt htmlContainer, string src, Dictionary <string, string> attributes, out string stylesheet, out CssData stylesheetData)
        {
            ArgChecker.AssertArgNotNull(htmlContainer, "htmlContainer");

            stylesheet     = null;
            stylesheetData = null;
            try
            {
                var args = new HtmlStylesheetLoadEventArgs(src, attributes);
                htmlContainer.RaiseHtmlStylesheetLoadEvent(args);

                if (!string.IsNullOrEmpty(args.SetStyleSheet))
                {
                    stylesheet = args.SetStyleSheet;
                }
                else if (args.SetStyleSheetData != null)
                {
                    stylesheetData = args.SetStyleSheetData;
                }
                else if (args.SetSrc != null)
                {
                    stylesheet = LoadStylesheet(htmlContainer, args.SetSrc);
                }
                else
                {
                    stylesheet = LoadStylesheet(htmlContainer, src);
                }
            }
            catch (Exception ex)
            {
                htmlContainer.ReportError(HtmlRenderErrorType.CssParsing, "Exception in handling stylesheet source", ex);
            }
        }
예제 #29
0
 /// <summary>
 /// Parse the given stylesheet to <see cref="CssData"/> object.<br/>
 /// If <paramref name="combineWithDefault"/> is true the parsed css blocks are added to the
 /// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned.
 /// </summary>
 /// <seealso cref="http://www.w3.org/TR/CSS21/sample.html"/>
 /// <param name="stylesheet">the stylesheet source to parse</param>
 /// <param name="combineWithDefault">true - combine the parsed css data with default css data, false - return only the parsed css data</param>
 /// <returns>the parsed css data</returns>
 public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault = true)
 {
     return(CssData.Parse(WinFormsAdapter.Instance, stylesheet, combineWithDefault));
 }
예제 #30
0
        /// <summary>
        /// Applies style to all boxes in the tree.<br/>
        /// If the html tag has style defined for each apply that style to the css box of the tag.<br/>
        /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/>
        /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/>
        /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/>
        /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/>
        /// </summary>
        /// <param name="box"></param>
        /// <param name="htmlContainer">the html container to use for reference resolve</param>
        /// <param name="cssData"> </param>
        /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param>
        private static void CascadeStyles(CssBox box, HtmlContainer htmlContainer, ref CssData cssData, ref bool cssDataChanged)
        {
            box.InheritStyle();

            if (box.HtmlTag != null)
            {
                // try assign style using the html element tag
                AssignCssBlocks(box, cssData, box.HtmlTag.Name);

                // try assign style using the "class" attribute of the html element
                if (box.HtmlTag.HasAttribute("class"))
                {
                    AssignClassCssBlocks(box, cssData);
                }

                // try assign style using the "id" attribute of the html element
                if (box.HtmlTag.HasAttribute("id"))
                {
                    var id = box.HtmlTag.TryGetAttribute("id");
                    AssignCssBlocks(box, cssData, "#" + id);
                }

                TranslateAttributes(box.HtmlTag, box);

                // Check for the style="" attribute
                if (box.HtmlTag.HasAttribute("style"))
                {
                    var block = CssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style"));
                    AssignCssBlock(box, block);
                }

                // Check for the <style> tag
                if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1)
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text.CutSubstring());
                }

                // Check for the <link rel=stylesheet> tag
                if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) &&
                    box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase))
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    var styleSheet = StylesheetLoadHelper.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes);
                    CssParser.ParseStyleSheet(cssData, styleSheet);
                }
            }

            // cascade text decoration only to boxes that actually have text so it will be handled correctly.
            if (box.TextDecoration != String.Empty && box.Text == null)
            {
                foreach (var childBox in box.Boxes)
                    childBox.TextDecoration = box.TextDecoration;
                box.TextDecoration = string.Empty;
            }

            foreach (var childBox in box.Boxes)
            {
                CascadeStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged);
            }
        }
예제 #31
0
 /// <summary>
 /// Measure the size (width and height) required to draw the given html under given max width restriction.<br/>
 /// If no max width restriction is given the layout will use the maximum possible width required by the content,
 /// it can be the longest text line or full image width.<br/>
 /// Use GDI+ text rending, use <see cref="Graphics.TextRenderingHint"/> to control text rendering.
 /// </summary>
 /// <param name="g">Device to use for measure</param>
 /// <param name="html">HTML source to render</param>
 /// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
 /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
 /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
 /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
 /// <returns>the size required for the html</returns>
 public static SizeF MeasureGdiPlus(Graphics g, string html, float maxWidth = 0, CssData cssData = null,
                                    EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
 {
     ArgChecker.AssertArgNotNull(g, "g");
     return(Measure(g, html, maxWidth, cssData, true, stylesheetLoad, imageLoad));
 }
예제 #32
0
 /// <summary>
 /// Write stylesheet data inline into the html.
 /// </summary>
 /// <param name="sb">the string builder to write stylesheet into</param>
 /// <param name="cssData">the css data to write to the head</param>
 /// <param name="indent">the indent to use for nice formating</param>
 private static void WriteStylesheet(StringBuilder sb, CssData cssData, int indent)
 {
     sb.Append(new string(' ', indent * 4));
     sb.AppendLine("<style type=\"text/css\">");
     foreach (var cssBlocks in cssData.MediaBlocks["all"])
     {
         sb.Append(new string(' ', (indent + 1) * 4));
         sb.Append(cssBlocks.Key);
         sb.Append(" { ");
         foreach (var cssBlock in cssBlocks.Value)
         {
             foreach (var property in cssBlock.Properties)
             {
                 // atodo: handle selectors
                 sb.AppendFormat("{0}: {1};", property.Key, property.Value);
             }
         }
         sb.Append(" }");
         sb.AppendLine();
     }
     sb.Append(new string(' ', indent * 4));
     sb.AppendLine("</style>");
 }
예제 #33
0
 /// <summary>
 /// Renders the specified HTML source on the specified location and max size restriction.<br/>
 /// Use GDI+ text rending, use <see cref="Graphics.TextRenderingHint"/> to control text rendering.<br/>
 /// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
 /// wrap as specified in the html<br/>
 /// Returned is the actual width and height of the rendered html.<br/>
 /// </summary>
 /// <param name="g">Device to render with</param>
 /// <param name="html">HTML source to render</param>
 /// <param name="left">optional: the left most location to start render the html at (default - 0)</param>
 /// <param name="top">optional: the top most location to start render the html at (default - 0)</param>
 /// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
 /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
 /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
 /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
 /// <returns>the actual size of the rendered html</returns>
 public static SizeF RenderGdiPlus(Graphics g, string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null,
                                   EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
 {
     ArgChecker.AssertArgNotNull(g, "g");
     return(RenderClip(g, html, new PointF(left, top), new SizeF(maxWidth, 0), cssData, true, stylesheetLoad, imageLoad));
 }
예제 #34
0
        /// <summary>
        /// Feeds the style with a block about the specific media.<br/>
        /// When no media is specified, "all" will be used.
        /// </summary>
        /// <param name="cssData"> </param>
        /// <param name="block">the CSS block to handle</param>
        /// <param name="media">optional: the media (default - all)</param>
        private void FeedStyleBlock(CssData cssData, string block, string media = "all")
        {
            int startIdx = block.IndexOf("{", StringComparison.Ordinal);
            int endIdx = startIdx > -1 ? block.IndexOf("}", startIdx) : -1;
            if (startIdx > -1 && endIdx > -1)
            {
                string blockSource = block.Substring(startIdx + 1, endIdx - startIdx - 1);
                var classes = block.Substring(0, startIdx).Split(',');

                foreach (string cls in classes)
                {
                    string className = cls.Trim(_cssClassTrimChars);
                    if (!String.IsNullOrEmpty(className))
                    {
                        var newblock = ParseCssBlockImp(className, blockSource);
                        if (newblock != null)
                        {
                            cssData.AddCssBlock(media, newblock);
                        }
                    }
                }
            }
        }
예제 #35
0
 /// <summary>
 /// Renders the specified HTML source on the specified location and max size restriction.<br/>
 /// Use GDI+ text rending, use <see cref="Graphics.TextRenderingHint"/> to control text rendering.<br/>
 /// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
 /// wrap as specified in the html<br/>
 /// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
 /// given max height not rendering the html below it.<br/>
 /// Returned is the actual width and height of the rendered html.<br/>
 /// </summary>
 /// <param name="g">Device to render with</param>
 /// <param name="html">HTML source to render</param>
 /// <param name="location">the top-left most location to start render the html at</param>
 /// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
 /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
 /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
 /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
 /// <returns>the actual size of the rendered html</returns>
 public static SizeF RenderGdiPlus(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData = null,
                                   EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
 {
     ArgChecker.AssertArgNotNull(g, "g");
     return(RenderClip(g, html, location, maxSize, cssData, true, stylesheetLoad, imageLoad));
 }
예제 #36
0
        /// <summary>
        /// Parse given stylesheet for CSS blocks<br/>
        /// This blocks are added under the "all" keyword.
        /// </summary>
        /// <param name="cssData">the CSS data to fill with parsed CSS objects</param>
        /// <param name="stylesheet">the stylesheet to parse</param>
        private void ParseStyleBlocks(CssData cssData, string stylesheet)
        {
            var startIdx = 0;
            int endIdx = 0;
            while (startIdx < stylesheet.Length && endIdx > -1)
            {
                endIdx = startIdx;
                while (endIdx + 1 < stylesheet.Length)
                {
                    endIdx++;
                    if (stylesheet[endIdx] == '}')
                        startIdx = endIdx + 1;
                    if (stylesheet[endIdx] == '{')
                        break;
                }

                int midIdx = endIdx + 1;
                if (endIdx > -1)
                {
                    endIdx++;
                    while (endIdx < stylesheet.Length)
                    {
                        if (stylesheet[endIdx] == '{')
                            startIdx = midIdx + 1;
                        if (stylesheet[endIdx] == '}')
                            break;
                        endIdx++;
                    }

                    if (endIdx < stylesheet.Length)
                    {
                        while (Char.IsWhiteSpace(stylesheet[startIdx]))
                            startIdx++;
                        var substring = stylesheet.Substring(startIdx, endIdx - startIdx + 1);
                        FeedStyleBlock(cssData, substring);
                    }
                    startIdx = endIdx + 1;
                }
            }
        }
예제 #37
0
        /// <summary>
        /// Renders the specified HTML on top of the given image.<br/>
        /// <paramref name="image"/> will contain the rendered html in it on top of original content.<br/>
        /// <paramref name="image"/> must not contain transparent pixels as it will corrupt the rendered html text.<br/>
        /// The HTML will be layout by the given image size but may be clipped if cannot fit.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </summary>
        /// <param name="image">the image to render the html on</param>
        /// <param name="html">HTML source to render</param>
        /// <param name="location">optional: the top-left most location to start render the html at (default - 0,0)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        public static void RenderToImage(Image image, string html, PointF location = new PointF(), CssData cssData = null,
                                         EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            ArgChecker.AssertArgNotNull(image, "image");
            var maxSize = new SizeF(image.Size.Width - location.X, image.Size.Height - location.Y);

            RenderToImage(image, html, location, maxSize, cssData, stylesheetLoad, imageLoad);
        }
예제 #38
0
        /// <summary>
        /// Applies style to all boxes in the tree.<br/>
        /// If the html tag has style defined for each apply that style to the css box of the tag.<br/>
        /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/>
        /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/>
        /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/>
        /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/>
        /// </summary>
        /// <param name="box"></param>
        /// <param name="bridge"> </param>
        /// <param name="cssData"> </param>
        /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param>
        private static void CascadeStyles(CssBox box, object bridge, ref CssData cssData, ref bool cssDataChanged)
        {
            box.InheritStyle();

            if (box.HtmlTag != null)
            {
                // try assign style using the html element tag
                AssignCssBlocks(box, cssData, box.HtmlTag.Name);

                // try assign style using the "class" attribute of the html element
                if (box.HtmlTag.HasAttribute("class"))
                {
                    AssignCssBlocks(box, cssData, "." + box.HtmlTag.Attributes["class"]);
                    AssignCssBlocks(box, cssData, box.HtmlTag.Name + "." + box.HtmlTag.Attributes["class"]);
                }

                // try assign style using the "id" attribute of the html element
                if (box.HtmlTag.HasAttribute("id"))
                {
                    AssignCssBlocks(box, cssData, "#" + box.HtmlTag.Attributes["id"]);
                }

                HtmlParser.TranslateAttributes(box.HtmlTag, box);

                // Check for the style="" attribute
                if (box.HtmlTag.HasAttribute("style"))
                {
                    var block = CssParser.ParseCssBlockImp(box.HtmlTag.Name, box.HtmlTag.Attributes["style"]);
                    AssignCssBlock(box, block);
                }

                // Check for the <style> tag
                if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1)
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text);
                }

                // Check for the <link rel=stylesheet> tag
                if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) &&
                    box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase))
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    var styleSheet = CssValueParser.GetStyleSheet(box.GetAttribute("href", string.Empty), bridge);
                    CssParser.ParseStyleSheet(cssData, styleSheet);
                }
            }

            foreach (var childBox in box.Boxes)
            {
                CascadeStyles(childBox, bridge, ref cssData, ref cssDataChanged);
            }
        }
예제 #39
0
        /// <summary>
        /// Renders the specified HTML on top of the given image.<br/>
        /// <paramref name="image"/> will contain the rendered html in it on top of original content.<br/>
        /// <paramref name="image"/> must not contain transparent pixels as it will corrupt the rendered html text.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </summary>
        /// <param name="image">the image to render the html on</param>
        /// <param name="html">HTML source to render</param>
        /// <param name="location">the top-left most location to start render the html at</param>
        /// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        public static void RenderToImage(Image image, string html, PointF location, SizeF maxSize, CssData cssData = null,
                                         EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            ArgChecker.AssertArgNotNull(image, "image");

            if (!string.IsNullOrEmpty(html))
            {
                // create memory buffer from desktop handle that supports alpha channel
                IntPtr dib;
                var    memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
                try
                {
                    // create memory buffer graphics to use for HTML rendering
                    using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
                    {
                        // draw the image to the memory buffer to be the background of the rendered html
                        memoryGraphics.DrawImageUnscaled(image, 0, 0);

                        // render HTML into the memory buffer
                        RenderHtml(memoryGraphics, html, location, maxSize, cssData, false, stylesheetLoad, imageLoad);
                    }

                    // copy from memory buffer to image
                    CopyBufferToImage(memoryHdc, image);
                }
                finally
                {
                    Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
                }
            }
        }
예제 #40
0
        /// <summary>
        /// Renders the specified HTML into a new image of the requested size.<br/>
        /// The HTML will be layout by the given size but will be clipped if cannot fit.<br/>
        /// <p>
        /// Limitation: The image cannot have transparent background, by default it will be white.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </p>
        /// </summary>
        /// <param name="html">HTML source to render</param>
        /// <param name="size">The size of the image to render into, layout html by width and clipped by height</param>
        /// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        /// <exception cref="ArgumentOutOfRangeException">if <paramref name="backgroundColor"/> is <see cref="Color.Transparent"/></exception>.
        public static Image RenderToImage(string html, Size size, Color backgroundColor             = new Color(), CssData cssData = null,
                                          EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            if (backgroundColor == Color.Transparent)
            {
                throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported");
            }

            // create the final image to render into
            var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);

            if (!string.IsNullOrEmpty(html))
            {
                // create memory buffer from desktop handle that supports alpha channel
                IntPtr dib;
                var    memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
                try
                {
                    // create memory buffer graphics to use for HTML rendering
                    using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
                    {
                        memoryGraphics.Clear(backgroundColor != Color.Empty ? backgroundColor : Color.White);

                        // render HTML into the memory buffer
                        RenderHtml(memoryGraphics, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad);
                    }

                    // copy from memory buffer to image
                    CopyBufferToImage(memoryHdc, image);
                }
                finally
                {
                    Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
                }
            }

            return(image);
        }
예제 #41
0
 /// <summary>
 /// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.<br/>
 /// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
 /// wrap as specified in the html<br/>
 /// If <paramref name="maxHeight"/> is zero the html will use all the required height, otherwise it will clip at the
 /// given max height not rendering the html below it.<br/>
 /// <p>
 /// Limitation: The image cannot have transparent background, by default it will be white.<br/>
 /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
 /// </p>
 /// </summary>
 /// <param name="html">HTML source to render</param>
 /// <param name="maxWidth">optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
 /// <param name="maxHeight">optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
 /// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
 /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
 /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
 /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
 /// <returns>the generated image of the html</returns>
 /// <exception cref="ArgumentOutOfRangeException">if <paramref name="backgroundColor"/> is <see cref="Color.Transparent"/></exception>.
 public static Image RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null,
                                   EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
 {
     return(RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad));
 }
예제 #42
0
        /// <summary>
        /// Set the selected text style (selection text color and background color).
        /// </summary>
        /// <param name="htmlContainer"> </param>
        /// <param name="cssData">the style data</param>
        private void SetTextSelectionStyle(HtmlContainerInt htmlContainer, CssData cssData)
        {
            htmlContainer.SelectionForeColor = RColor.Empty;
            htmlContainer.SelectionBackColor = RColor.Empty;

            if (cssData.ContainsCssBlock("::selection"))
            {
                var blocks = cssData.GetCssBlock("::selection");
                foreach (var block in blocks)
                {
                    if (block.Properties.ContainsKey("color"))
                        htmlContainer.SelectionForeColor = _cssParser.ParseColor(block.Properties["color"]);
                    if (block.Properties.ContainsKey("background-color"))
                        htmlContainer.SelectionBackColor = _cssParser.ParseColor(block.Properties["background-color"]);
                }
            }
        }
예제 #43
0
        /// <summary>
        /// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.<br/>
        /// If <paramref name="maxSize.Width"/> is zero the html will use all the required width, otherwise it will perform line
        /// wrap as specified in the html<br/>
        /// If <paramref name="maxSize.Height"/> is zero the html will use all the required height, otherwise it will clip at the
        /// given max height not rendering the html below it.<br/>
        /// If <paramref name="minSize"/> (Width/Height) is above zero the rendered image will not be smaller than the given min size.<br/>
        /// <p>
        /// Limitation: The image cannot have transparent background, by default it will be white.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </p>
        /// </summary>
        /// <param name="html">HTML source to render</param>
        /// <param name="minSize">optional: the min size of the rendered html (zero - not limit the width/height)</param>
        /// <param name="maxSize">optional: the max size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height)</param>
        /// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        /// <exception cref="ArgumentOutOfRangeException">if <paramref name="backgroundColor"/> is <see cref="Color.Transparent"/></exception>.
        public static Image RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null,
                                          EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad      = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            if (backgroundColor == Color.Transparent)
            {
                throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported");
            }

            if (string.IsNullOrEmpty(html))
            {
                return(new Bitmap(0, 0, PixelFormat.Format32bppArgb));
            }

            using (var container = new HtmlContainer())
            {
                container.AvoidAsyncImagesLoading = true;
                container.AvoidImagesLateLoading  = true;

                if (stylesheetLoad != null)
                {
                    container.StylesheetLoad += stylesheetLoad;
                }
                if (imageLoad != null)
                {
                    container.ImageLoad += imageLoad;
                }
                container.SetHtml(html, cssData);

                var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize);
                container.MaxSize = finalSize;

                // create the final image to render into by measured size
                var image = new Bitmap(finalSize.Width, finalSize.Height, PixelFormat.Format32bppArgb);

                // create memory buffer from desktop handle that supports alpha channel
                IntPtr dib;
                var    memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
                try
                {
                    // render HTML into the memory buffer
                    using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
                    {
                        memoryGraphics.Clear(backgroundColor != Color.Empty ? backgroundColor : Color.White);
                        container.PerformPaint(memoryGraphics);
                    }

                    // copy from memory buffer to image
                    CopyBufferToImage(memoryHdc, image);
                }
                finally
                {
                    Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
                }

                return(image);
            }
        }
예제 #44
0
 /// <summary>
 /// Clone css data if it has not already been cloned.<br/>
 /// Used to preserve the base css data used when changed by style inside html.
 /// </summary>
 private static void CloneCssData(ref CssData cssData, ref bool cssDataChanged)
 {
     if (!cssDataChanged)
     {
         cssDataChanged = true;
         cssData = cssData.Clone();
     }
 }
예제 #45
0
        /// <summary>
        /// Renders the specified HTML into a new image of the requested size.<br/>
        /// The HTML will be layout by the given size but will be clipped if cannot fit.<br/>
        /// The generated image have transparent background that the html is rendered on.<br/>
        /// GDI+ text rending can be controlled by providing <see cref="TextRenderingHint"/>.<br/>
        /// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
        /// </summary>
        /// <param name="html">HTML source to render</param>
        /// <param name="size">The size of the image to render into, layout html by width and clipped by height</param>
        /// <param name="textRenderingHint">optional: (default - SingleBitPerPixelGridFit)</param>
        /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
        /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
        /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
        /// <returns>the generated image of the html</returns>
        public static Image RenderToImageGdiPlus(string html, Size size, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData          = null,
                                                 EventHandler <HtmlStylesheetLoadEventArgs> stylesheetLoad   = null, EventHandler <HtmlImageLoadEventArgs> imageLoad = null)
        {
            var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);

            using (var g = Graphics.FromImage(image))
            {
                g.TextRenderingHint = textRenderingHint;
                RenderHtml(g, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad);
            }

            return(image);
        }
예제 #46
0
        /// <summary>
        /// Read styles defined inside the dom structure in links and style elements.<br/>
        /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/>
        /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/>
        /// </summary>
        /// <param name="box">the box to parse style data in</param>
        /// <param name="htmlContainer">the html container to use for reference resolve</param>
        /// <param name="cssData">the style data to fill with found styles</param>
        /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param>
        private void CascadeParseStyles(CssBox box, HtmlContainerInt htmlContainer, ref CssData cssData, ref bool cssDataChanged)
        {
            if (box.HtmlTag != null)
            {
                // Check for the <link rel=stylesheet> tag
                if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) &&
                    box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase))
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    string stylesheet;
                    CssData stylesheetData;
                    StylesheetLoadHandler.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes, out stylesheet, out stylesheetData);
                    if (stylesheet != null)
                        _cssParser.ParseStyleSheet(cssData, stylesheet);
                    else if (stylesheetData != null)
                        cssData.Combine(stylesheetData);
                }

                // Check for the <style> tag
                if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count > 0)
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    foreach (var child in box.Boxes)
                        _cssParser.ParseStyleSheet(cssData, child.Text.CutSubstring());
                }
            }

            foreach (var childBox in box.Boxes)
            {
                CascadeParseStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged);
            }
        }
예제 #47
0
        /// <summary>
        /// Generate css tree by parsing the given html and applying the given css style data on it.
        /// </summary>
        /// <param name="html">the html to parse</param>
        /// <param name="htmlContainer">the html container to use for reference resolve</param>
        /// <param name="cssData">the css data to use</param>
        /// <returns>the root of the generated tree</returns>
        public static CssBox GenerateCssTree(string html, HtmlContainer htmlContainer, ref CssData cssData)
        {
            var root = HtmlParser.ParseDocument(html);

            if (root != null)
            {
                bool cssDataChanged = false;
                CascadeStyles(root, htmlContainer, ref cssData, ref cssDataChanged);

                CorrectTextBoxes(root);

                CorrectLineBreaksBlocks(root);

                CorrectInlineBoxesParent(root);

                CorrectBlockInsideInline(root);

                CorrectInlineBoxesParent(root);
            }
            return(root);
        }