/// <summary>
        /// Measure the size of the html by performing layout under the given restrictions.
        /// </summary>
        /// <param name="g">the graphics to use</param>
        /// <param name="htmlContainer">the html to calculate the layout for</param>
        /// <param name="minSize">the minimal size of the rendered html (zero - not limit the width/height)</param>
        /// <param name="maxSize">the maximum 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>
        /// <returns>return: the size of the html to be rendered within the min/max limits</returns>
        public static RSize MeasureHtmlByRestrictions(RGraphics g, HtmlContainerInt htmlContainer, RSize minSize, RSize maxSize)
        {
            // first layout without size restriction to know html actual size
            htmlContainer.PerformLayout(g);

            if (maxSize.Width > 0 && maxSize.Width < htmlContainer.ActualSize.Width)
            {
                // to allow the actual size be smaller than max we need to set max size only if it is really larger
                htmlContainer.MaxSize = new RSize(maxSize.Width, 0);
                htmlContainer.PerformLayout(g);
            }

            // restrict the final size by min/max
            var finalWidth = Math.Max(maxSize.Width > 0 ? Math.Min(maxSize.Width, (int)htmlContainer.ActualSize.Width) : (int)htmlContainer.ActualSize.Width, minSize.Width);

            // if the final width is larger than the actual we need to re-layout so the html can take the full given width.
            if (finalWidth > htmlContainer.ActualSize.Width)
            {
                htmlContainer.MaxSize = new RSize(finalWidth, 0);
                htmlContainer.PerformLayout(g);
            }

            var finalHeight = Math.Max(maxSize.Height > 0 ? Math.Min(maxSize.Height, (int)htmlContainer.ActualSize.Height) : (int)htmlContainer.ActualSize.Height, minSize.Height);

            return new RSize(finalWidth, finalHeight);
        }
 /// <summary>
 /// Clip the region the graphics will draw on by the overflow style of the containing block.<br/>
 /// Recursively travel up the tree to find containing block that has overflow style set to hidden. if not
 /// block found there will be no clipping and null will be returned.
 /// </summary>
 /// <param name="g">the graphics to clip</param>
 /// <param name="box">the box that is rendered to get containing blocks</param>
 /// <returns>true - was clipped, false - not clipped</returns>
 public static bool ClipGraphicsByOverflow(RGraphics g, CssBox box)
 {
     var containingBlock = box.ContainingBlock;
     while (true)
     {
         if (containingBlock.Overflow == CssConstants.Hidden)
         {
             var prevClip = g.GetClip();
             var rect = box.ContainingBlock.ClientRectangle;
             rect.X -= 2; // TODO:a find better way to fix it
             rect.Width += 2;
             rect.Offset(box.HtmlContainer.ScrollOffset);
             rect.Intersect(prevClip);
             g.PushClip(rect);
             return true;
         }
         else
         {
             var cBlock = containingBlock.ContainingBlock;
             if (cBlock == containingBlock)
                 return false;
             containingBlock = cBlock;
         }
     }
 }
        /// <summary>
        /// Paints the fragment
        /// </summary>
        /// <param name="g">the device to draw to</param>
        protected override void PaintImp(RGraphics g)
        {
            // load image iff it is in visible rectangle
            if (_imageLoadHandler == null)
            {
                _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnLoadImageComplete);
                _imageLoadHandler.LoadImage(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null);
            }

            var rect = CommonUtils.GetFirstValueOrDefault(Rectangles);
            RPoint offset = HtmlContainer.ScrollOffset;
            rect.Offset(offset);

            var clipped = RenderUtils.ClipGraphicsByOverflow(g, this);

            PaintBackground(g, rect, true, true);
            BordersDrawHandler.DrawBoxBorders(g, this, rect, true, true);

            RRect r = _imageWord.Rectangle;
            r.Offset(offset);
            r.Height -= ActualBorderTopWidth + ActualBorderBottomWidth + ActualPaddingTop + ActualPaddingBottom;
            r.Y += ActualBorderTopWidth + ActualPaddingTop;
            r.X = Math.Floor(r.X);
            r.Y = Math.Floor(r.Y);

            if (_imageWord.Image != null)
            {
                if (r.Width > 0 && r.Height > 0)
                {
                    if (_imageWord.ImageRectangle == RRect.Empty)
                        g.DrawImage(_imageWord.Image, r);
                    else
                        g.DrawImage(_imageWord.Image, r, _imageWord.ImageRectangle);

                    if (_imageWord.Selected)
                    {
                        g.DrawRectangle(GetSelectionBackBrush(g, true), _imageWord.Left + offset.X, _imageWord.Top + offset.Y, _imageWord.Width + 2, DomUtils.GetCssLineBoxByWord(_imageWord).LineHeight);
                    }
                }
            }
            else if (_imageLoadingComplete)
            {
                if (_imageLoadingComplete && r.Width > 19 && r.Height > 19)
                {
                    RenderUtils.DrawImageErrorIcon(g, HtmlContainer, r);
                }
            }
            else
            {
                RenderUtils.DrawImageLoadingIcon(g, HtmlContainer, r);
                if (r.Width > 19 && r.Height > 19)
                {
                    g.DrawRectangle(g.GetPen(RColor.LightGray), r.X, r.Y, r.Width, r.Height);
                }
            }

            if (clipped)
                g.PopClip();
        }
Example #4
0
 /// <summary>
 /// Gets the white space width of the specified box
 /// </summary>
 /// <param name="g"></param>
 /// <param name="box"></param>
 /// <returns></returns>
 public static double WhiteSpace(RGraphics g, CssBoxProperties box)
 {
     double w = box.ActualFont.GetWhitespaceWidth(g);
     if (!(String.IsNullOrEmpty(box.WordSpacing) || box.WordSpacing == CssConstants.Normal))
     {
         w += CssValueParser.ParseLength(box.WordSpacing, 0, box, true);
     }
     return w;
 }
Example #5
0
        /// <summary>
        /// Measures the bounds of box and children, recursively.<br/>
        /// Performs layout of the DOM structure creating lines by set bounds restrictions.
        /// </summary>
        /// <param name="g">Device context to use</param>
        protected override void PerformLayoutImp(RGraphics g)
        {
            if (Display == CssConstants.None)
                return;

            RectanglesReset();

            var prevSibling = DomUtils.GetPreviousSibling(this);
            double left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth;
            double top = (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0) + MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0);
            Location = new RPoint(left, top);
            ActualBottom = top;

            //width at 100% (or auto)
            double minwidth = GetMinimumWidth();
            double width = ContainingBlock.Size.Width
                           - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight
                           - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth
                           - ActualMarginLeft - ActualMarginRight - ActualBorderLeftWidth - ActualBorderRightWidth;

            //Check width if not auto
            if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width))
            {
                width = CssValueParser.ParseLength(Width, width, this);
            }

            if (width < minwidth || width >= 9999)
                width = minwidth;

            double height = ActualHeight;
            if (height < 1)
            {
                height = Size.Height + ActualBorderTopWidth + ActualBorderBottomWidth;
            }
            if (height < 1)
            {
                height = 2;
            }
            if (height <= 2 && ActualBorderTopWidth < 1 && ActualBorderBottomWidth < 1)
            {
                BorderTopStyle = BorderBottomStyle = CssConstants.Solid;
                BorderTopWidth = "1px";
                BorderBottomWidth = "1px";
            }

            Size = new RSize(width, height);

            ActualBottom = Location.Y + ActualPaddingTop + ActualPaddingBottom + height;
        }
        /// <summary>
        /// Perform the layout of the html container by given size restrictions returning the final size.<br/>
        /// The layout can be effected by the HTML content in the <paramref name="htmlContainer"/> if <paramref name="autoSize"/> or
        /// <paramref name="autoSizeHeightOnly"/> is set to true.<br/>
        /// Handle minimum and maximum size restrictions.<br/>
        /// Handle auto size and auto size for height only. if <paramref name="autoSize"/> is true <paramref name="autoSizeHeightOnly"/>
        /// is ignored.<br/>
        /// </summary>
        /// <param name="g">the graphics used for layout</param>
        /// <param name="htmlContainer">the html container to layout</param>
        /// <param name="size">the current size</param>
        /// <param name="minSize">the min size restriction - can be empty for no restriction</param>
        /// <param name="maxSize">the max size restriction - can be empty for no restriction</param>
        /// <param name="autoSize">if to modify the size (width and height) by html content layout</param>
        /// <param name="autoSizeHeightOnly">if to modify the height by html content layout</param>
        public static RSize Layout(RGraphics g, HtmlContainerInt htmlContainer, RSize size, RSize minSize, RSize maxSize, bool autoSize, bool autoSizeHeightOnly)
        {
            if (autoSize)
                htmlContainer.MaxSize = new RSize(0, 0);
            else if (autoSizeHeightOnly)
                htmlContainer.MaxSize = new RSize(size.Width, 0);
            else
                htmlContainer.MaxSize = size;

            htmlContainer.PerformLayout(g);

            RSize newSize = size;
            if (autoSize || autoSizeHeightOnly)
            {
                if (autoSize)
                {
                    if (maxSize.Width > 0 && maxSize.Width < htmlContainer.ActualSize.Width)
                    {
                        // to allow the actual size be smaller than max we need to set max size only if it is really larger
                        htmlContainer.MaxSize = maxSize;
                        htmlContainer.PerformLayout(g);
                    }
                    else if (minSize.Width > 0 && minSize.Width > htmlContainer.ActualSize.Width)
                    {
                        // if min size is larger than the actual we need to re-layout so all 100% layouts will be correct
                        htmlContainer.MaxSize = new RSize(minSize.Width, 0);
                        htmlContainer.PerformLayout(g);
                    }
                    newSize = htmlContainer.ActualSize;
                }
                else if (Math.Abs(size.Height - htmlContainer.ActualSize.Height) > 0.01)
                {
                    var prevWidth = size.Width;

                    // make sure the height is not lower than min if given
                    newSize.Height = minSize.Height > 0 && minSize.Height > htmlContainer.ActualSize.Height
                        ? minSize.Height
                        : htmlContainer.ActualSize.Height;

                    // handle if changing the height of the label affects the desired width and those require re-layout
                    if (Math.Abs(prevWidth - size.Width) > 0.01)
                        return Layout(g, htmlContainer, size, minSize, maxSize, false, true);
                }
            }

            return newSize;
        }
        /// <summary>
        /// Draw the background image of the given box in the given rectangle.<br/>
        /// Handle background-repeat and background-position values.
        /// </summary>
        /// <param name="g">the device to draw into</param>
        /// <param name="box">the box to draw its background image</param>
        /// <param name="imageLoadHandler">the handler that loads image to draw</param>
        /// <param name="rectangle">the rectangle to draw image in</param>
        public static void DrawBackgroundImage(RGraphics g, CssBox box, ImageLoadHandler imageLoadHandler, RRect rectangle)
        {
            // image size depends if specific rectangle given in image loader
            var imgSize = new RSize(imageLoadHandler.Rectangle == RRect.Empty ? imageLoadHandler.Image.Width : imageLoadHandler.Rectangle.Width,
                imageLoadHandler.Rectangle == RRect.Empty ? imageLoadHandler.Image.Height : imageLoadHandler.Rectangle.Height);

            // get the location by BackgroundPosition value
            var location = GetLocation(box.BackgroundPosition, rectangle, imgSize);

            var srcRect = imageLoadHandler.Rectangle == RRect.Empty
                ? new RRect(0, 0, imgSize.Width, imgSize.Height)
                : new RRect(imageLoadHandler.Rectangle.Left, imageLoadHandler.Rectangle.Top, imgSize.Width, imgSize.Height);

            // initial image destination rectangle
            var destRect = new RRect(location, imgSize);

            // need to clip so repeated image will be cut on rectangle
            var lRectangle = rectangle;
            lRectangle.Intersect(g.GetClip());
            g.PushClip(lRectangle);

            switch (box.BackgroundRepeat)
            {
                case "no-repeat":
                    g.DrawImage(imageLoadHandler.Image, destRect, srcRect);
                    break;
                case "repeat-x":
                    DrawRepeatX(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize);
                    break;
                case "repeat-y":
                    DrawRepeatY(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize);
                    break;
                default:
                    DrawRepeat(g, imageLoadHandler, rectangle, srcRect, destRect, imgSize);
                    break;
            }

            g.PopClip();
        }
 /// <summary>
 /// Draws all the border of the box with respect to style, width, etc.
 /// </summary>
 /// <param name="g">the device to draw into</param>
 /// <param name="box">the box to draw borders for</param>
 /// <param name="rect">the bounding rectangle to draw in</param>
 /// <param name="isFirst">is it the first rectangle of the element</param>
 /// <param name="isLast">is it the last rectangle of the element</param>
 public static void DrawBoxBorders(RGraphics g, CssBox box, RRect rect, bool isFirst, bool isLast)
 {
     if (rect.Width > 0 && rect.Height > 0)
     {
         if (!(string.IsNullOrEmpty(box.BorderTopStyle) || box.BorderTopStyle == CssConstants.None || box.BorderTopStyle == CssConstants.Hidden) && box.ActualBorderTopWidth > 0)
         {
             DrawBorder(Border.Top, box, g, rect, isFirst, isLast);
         }
         if (isFirst && !(string.IsNullOrEmpty(box.BorderLeftStyle) || box.BorderLeftStyle == CssConstants.None || box.BorderLeftStyle == CssConstants.Hidden) && box.ActualBorderLeftWidth > 0)
         {
             DrawBorder(Border.Left, box, g, rect, true, isLast);
         }
         if (!(string.IsNullOrEmpty(box.BorderBottomStyle) || box.BorderBottomStyle == CssConstants.None || box.BorderBottomStyle == CssConstants.Hidden) && box.ActualBorderBottomWidth > 0)
         {
             DrawBorder(Border.Bottom, box, g, rect, isFirst, isLast);
         }
         if (isLast && !(string.IsNullOrEmpty(box.BorderRightStyle) || box.BorderRightStyle == CssConstants.None || box.BorderRightStyle == CssConstants.Hidden) && box.ActualBorderRightWidth > 0)
         {
             DrawBorder(Border.Right, box, g, rect, isFirst, true);
         }
     }
 }
 /// <summary>
 /// Get pen to be used for border draw respecting its style.
 /// </summary>
 private static RPen GetPen(RGraphics g, string style, RColor color, double width)
 {
     var p = g.GetPen(color);
     p.Width = width;
     switch (style)
     {
         case "solid":
             p.DashStyle = RDashStyle.Solid;
             break;
         case "dotted":
             p.DashStyle = RDashStyle.Dot;
             break;
         case "dashed":
             p.DashStyle = RDashStyle.Dash;
             break;
     }
     return p;
 }
        /// <summary>
        /// Makes a border path for rounded borders.<br/>
        /// To support rounded dotted/dashed borders we need to use arc in the border path.<br/>
        /// Return null if the border is not rounded.<br/>
        /// </summary>
        /// <param name="g">the device to draw into</param>
        /// <param name="border">Desired border</param>
        /// <param name="b">Box which the border corresponds</param>
        /// <param name="r">the rectangle the border is enclosing</param>
        /// <returns>Beveled border path, null if there is no rounded corners</returns>
        private static RGraphicsPath GetRoundedBorderPath(RGraphics g, Border border, CssBox b, RRect r)
        {
            RGraphicsPath path = null;
            switch (border)
            {
                case Border.Top:
                    if (b.ActualCornerNw > 0 || b.ActualCornerNe > 0)
                    {
                        path = g.GetGraphicsPath();
                        path.Start(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw);

                        if (b.ActualCornerNw > 0)
                            path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft);

                        path.LineTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerNe, r.Top + b.ActualBorderTopWidth / 2);

                        if (b.ActualCornerNe > 0)
                            path.ArcTo(r.Right - b.ActualBorderRightWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight);
                    }
                    break;
                case Border.Bottom:
                    if (b.ActualCornerSw > 0 || b.ActualCornerSe > 0)
                    {
                        path = g.GetGraphicsPath();
                        path.Start(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe);

                        if (b.ActualCornerSe > 0)
                            path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight);

                        path.LineTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerSw, r.Bottom - b.ActualBorderBottomWidth / 2);

                        if (b.ActualCornerSw > 0)
                            path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft);
                    }
                    break;
                case Border.Right:
                    if (b.ActualCornerNe > 0 || b.ActualCornerSe > 0)
                    {
                        path = g.GetGraphicsPath();

                        bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden;
                        bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden;
                        path.Start(r.Right - b.ActualBorderRightWidth / 2 - (noTop ? b.ActualCornerNe : 0), r.Top + b.ActualBorderTopWidth / 2 + (noTop ? 0 : b.ActualCornerNe));

                        if (b.ActualCornerNe > 0 && noTop)
                            path.ArcTo(r.Right - b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight);

                        path.LineTo(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe);

                        if (b.ActualCornerSe > 0 && noBottom)
                            path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight);
                    }
                    break;
                case Border.Left:
                    if (b.ActualCornerNw > 0 || b.ActualCornerSw > 0)
                    {
                        path = g.GetGraphicsPath();

                        bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden;
                        bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden;
                        path.Start(r.Left + b.ActualBorderLeftWidth / 2 + (noBottom ? b.ActualCornerSw : 0), r.Bottom - b.ActualBorderBottomWidth / 2 - (noBottom ? 0 : b.ActualCornerSw));

                        if (b.ActualCornerSw > 0 && noBottom)
                            path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft);

                        path.LineTo(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw);

                        if (b.ActualCornerNw > 0 && noTop)
                            path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft);
                    }
                    break;
            }

            return path;
        }
Example #11
0
        /// <summary>
        /// Paints the fragment
        /// </summary>
        /// <param name="g">the device to draw to</param>
        protected virtual void PaintImp(RGraphics g)
        {
            if (Display != CssConstants.None && (Display != CssConstants.TableCell || EmptyCells != CssConstants.Hide || !IsSpaceOrEmpty))
            {
                var clipped = RenderUtils.ClipGraphicsByOverflow(g, this);

                var areas = Rectangles.Count == 0 ? new List<RRect>(new[] { Bounds }) : new List<RRect>(Rectangles.Values);

                RRect[] rects = areas.ToArray();
                RPoint offset = HtmlContainer.ScrollOffset;

                for (int i = 0; i < rects.Length; i++)
                {
                    var actualRect = rects[i];
                    actualRect.Offset(offset);

                    PaintBackground(g, actualRect, i == 0, i == rects.Length - 1);
                    BordersDrawHandler.DrawBoxBorders(g, this, actualRect, i == 0, i == rects.Length - 1);
                }

                PaintWords(g, offset);

                for (int i = 0; i < rects.Length; i++)
                {
                    var actualRect = rects[i];
                    actualRect.Offset(offset);
                    PaintDecoration(g, actualRect, i == 0, i == rects.Length - 1);
                }

                // split paint to handle z-order
                foreach (CssBox b in Boxes)
                {
                    if (b.Position != CssConstants.Absolute)
                        b.Paint(g);
                }
                foreach (CssBox b in Boxes)
                {
                    if (b.Position == CssConstants.Absolute)
                        b.Paint(g);
                }

                if (clipped)
                    g.PopClip();

                if (_listItemBox != null)
                {
                    _listItemBox.Paint(g);
                }
            }
        }
Example #12
0
        /// <summary>
        /// Paints the fragment
        /// </summary>
        /// <param name="g">the device to draw to</param>
        protected override void PaintImp(RGraphics g)
        {
            var offset = HtmlContainer != null ? HtmlContainer.ScrollOffset : RPoint.Empty;
            var rect = new RRect(Bounds.X + offset.X, Bounds.Y + offset.Y, Bounds.Width, Bounds.Height);

            if (rect.Height > 2 && RenderUtils.IsColorVisible(ActualBackgroundColor))
            {
                g.DrawRectangle(g.GetSolidBrush(ActualBackgroundColor), rect.X, rect.Y, rect.Width, rect.Height);
            }

            var b1 = g.GetSolidBrush(ActualBorderTopColor);
            BordersDrawHandler.DrawBorder(Border.Top, g, this, b1, rect);

            if (rect.Height > 1)
            {
                var b2 = g.GetSolidBrush(ActualBorderLeftColor);
                BordersDrawHandler.DrawBorder(Border.Left, g, this, b2, rect);

                var b3 = g.GetSolidBrush(ActualBorderRightColor);
                BordersDrawHandler.DrawBorder(Border.Right, g, this, b3, rect);

                var b4 = g.GetSolidBrush(ActualBorderBottomColor);
                BordersDrawHandler.DrawBorder(Border.Bottom, g, this, b4, rect);
            }
        }
        /// <summary>
        /// Draw specific border (top/bottom/left/right) with the box data (style/width/rounded).<br/>
        /// </summary>
        /// <param name="border">desired border to draw</param>
        /// <param name="box">the box to draw its borders, contain the borders data</param>
        /// <param name="g">the device to draw into</param>
        /// <param name="rect">the rectangle the border is enclosing</param>
        /// <param name="isLineStart">Specifies if the border is for a starting line (no bevel on left)</param>
        /// <param name="isLineEnd">Specifies if the border is for an ending line (no bevel on right)</param>
        private static void DrawBorder(Border border, CssBox box, RGraphics g, RRect rect, bool isLineStart, bool isLineEnd)
        {
            var style = GetStyle(border, box);
            var color = GetColor(border, box, style);

            var borderPath = GetRoundedBorderPath(g, border, box, rect);
            if (borderPath != null)
            {
                // rounded border need special path
                Object prevMode = null;
                if (box.HtmlContainer != null && !box.HtmlContainer.AvoidGeometryAntialias && box.IsRounded)
                    prevMode = g.SetAntiAliasSmoothingMode();

                var pen = GetPen(g, style, color, GetWidth(border, box));
                using (borderPath)
                    g.DrawPath(pen, borderPath);

                g.ReturnPreviousSmoothingMode(prevMode);
            }
            else
            {
                // non rounded border
                if (style == CssConstants.Inset || style == CssConstants.Outset)
                {
                    // inset/outset border needs special rectangle
                    SetInOutsetRectanglePoints(border, box, rect, isLineStart, isLineEnd);
                    g.DrawPolygon(g.GetSolidBrush(color), _borderPts);
                }
                else
                {
                    // solid/dotted/dashed border draw as simple line
                    var pen = GetPen(g, style, color, GetWidth(border, box));
                    switch (border)
                    {
                        case Border.Top:
                            g.DrawLine(pen, Math.Ceiling(rect.Left), rect.Top + box.ActualBorderTopWidth / 2, rect.Right - 1, rect.Top + box.ActualBorderTopWidth / 2);
                            break;
                        case Border.Left:
                            g.DrawLine(pen, rect.Left + box.ActualBorderLeftWidth / 2, Math.Ceiling(rect.Top), rect.Left + box.ActualBorderLeftWidth / 2, Math.Floor(rect.Bottom));
                            break;
                        case Border.Bottom:
                            g.DrawLine(pen, Math.Ceiling(rect.Left), rect.Bottom - box.ActualBorderBottomWidth / 2, rect.Right - 1, rect.Bottom - box.ActualBorderBottomWidth / 2);
                            break;
                        case Border.Right:
                            g.DrawLine(pen, rect.Right - box.ActualBorderRightWidth / 2, Math.Ceiling(rect.Top), rect.Right - box.ActualBorderRightWidth / 2, Math.Floor(rect.Bottom));
                            break;
                    }
                }
            }
        }
Example #14
0
        /// <summary>
        /// Measures the bounds of box and children, recursively.<br/>
        /// Performs layout of the DOM structure creating lines by set bounds restrictions.<br/>
        /// </summary>
        /// <param name="g">Device context to use</param>
        protected virtual void PerformLayoutImp(RGraphics g)
        {
            if (Display != CssConstants.None)
            {
                RectanglesReset();
                MeasureWordsSize(g);
            }

            if (IsBlock || Display == CssConstants.ListItem || Display == CssConstants.Table || Display == CssConstants.InlineTable || Display == CssConstants.TableCell)
            {
                // Because their width and height are set by CssTable
                if (Display != CssConstants.TableCell && Display != CssConstants.Table)
                {
                    double width = ContainingBlock.Size.Width
                                   - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight
                                   - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth;

                    if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width))
                    {
                        width = CssValueParser.ParseLength(Width, width, this);
                    }

                    Size = new RSize(width, Size.Height);

                    // must be separate because the margin can be calculated by percentage of the width
                    Size = new RSize(width - ActualMarginLeft - ActualMarginRight, Size.Height);
                }

                if (Display != CssConstants.TableCell)
                {
                    var prevSibling = DomUtils.GetPreviousSibling(this);
                    double left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth;
                    double top = (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0) + MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0);
                    Location = new RPoint(left, top);
                    ActualBottom = top;
                }

                //If we're talking about a table here..
                if (Display == CssConstants.Table || Display == CssConstants.InlineTable)
                {
                    CssLayoutEngineTable.PerformLayout(g, this);
                }
                else
                {
                    //If there's just inline boxes, create LineBoxes
                    if (DomUtils.ContainsInlinesOnly(this))
                    {
                        ActualBottom = Location.Y;
                        CssLayoutEngine.CreateLineBoxes(g, this); //This will automatically set the bottom of this block
                    }
                    else if (_boxes.Count > 0)
                    {
                        foreach (var childBox in Boxes)
                        {
                            childBox.PerformLayout(g);
                        }
                        ActualRight = CalculateActualRight();
                        ActualBottom = MarginBottomCollapse();
                    }
                }
            }
            else
            {
                var prevSibling = DomUtils.GetPreviousSibling(this);
                if (prevSibling != null)
                {
                    if (Location == RPoint.Empty)
                        Location = prevSibling.Location;
                    ActualBottom = prevSibling.ActualBottom;
                }
            }
            ActualBottom = Math.Max(ActualBottom, Location.Y + ActualHeight);

            CreateListItemBox(g);

            var actualWidth = Math.Max(GetMinimumWidth() + GetWidthMarginDeep(this), Size.Width < 90999 ? ActualRight - HtmlContainer.Root.Location.X : 0);
            HtmlContainer.ActualSize = CommonUtils.Max(HtmlContainer.ActualSize, new RSize(actualWidth, ActualBottom - HtmlContainer.Root.Location.Y));
        }
Example #15
0
        /// <summary>
        /// Paints the fragment
        /// </summary>
        /// <param name="g">Device context to use</param>
        public void Paint(RGraphics g)
        {
            try
            {
                if (Display != CssConstants.None && Visibility == CssConstants.Visible)
                {
                    // don't call paint if the rectangle of the box is not in visible rectangle
                    bool visible = Rectangles.Count == 0;
                    if (!visible)
                    {
                        var clip = g.GetClip();
                        var rect = ContainingBlock.ClientRectangle;
                        rect.X -= 2;
                        rect.Width += 2;
                        rect.Offset(new RPoint(-HtmlContainer.Location.X, -HtmlContainer.Location.Y));
                        rect.Offset(HtmlContainer.ScrollOffset);
                        clip.Intersect(rect);

                        if (clip != RRect.Empty)
                            visible = true;
                    }

                    if (visible)
                        PaintImp(g);
                }
            }
            catch (Exception ex)
            {
                HtmlContainer.ReportError(HtmlRenderErrorType.Paint, "Exception in box paint", ex);
            }
        }
Example #16
0
 /// <summary>
 /// Measures the bounds of box and children, recursively.<br/>
 /// Performs layout of the DOM structure creating lines by set bounds restrictions.
 /// </summary>
 /// <param name="g">Device context to use</param>
 public void PerformLayout(RGraphics g)
 {
     try
     {
         PerformLayoutImp(g);
     }
     catch (Exception ex)
     {
         HtmlContainer.ReportError(HtmlRenderErrorType.Layout, "Exception in box layout", ex);
     }
 }
Example #17
0
 /// <summary>
 /// Get brush for selection background depending if it has external and if alpha is required for images.
 /// </summary>
 /// <param name="g"></param>
 /// <param name="forceAlpha">used for images so they will have alpha effect</param>
 protected RBrush GetSelectionBackBrush(RGraphics g, bool forceAlpha)
 {
     var backColor = HtmlContainer.SelectionBackColor;
     if (backColor != RColor.Empty)
     {
         if (forceAlpha && backColor.A > 180)
             return g.GetSolidBrush(RColor.FromArgb(180, backColor.R, backColor.G, backColor.B));
         else
             return g.GetSolidBrush(backColor);
     }
     else
     {
         return g.GetSolidBrush(CssUtils.DefaultSelectionBackcolor);
     }
 }
Example #18
0
        /// <summary>
        /// Paints the text decoration (underline/strike-through/over-line)
        /// </summary>
        /// <param name="g">the device to draw into</param>
        /// <param name="rectangle"> </param>
        /// <param name="isFirst"> </param>
        /// <param name="isLast"> </param>
        protected void PaintDecoration(RGraphics g, RRect rectangle, bool isFirst, bool isLast)
        {
            if (string.IsNullOrEmpty(TextDecoration) || TextDecoration == CssConstants.None)
                return;

            double y = 0f;
            if (TextDecoration == CssConstants.Underline)
            {
                y = Math.Round(rectangle.Top + ActualFont.UnderlineOffset);
            }
            else if (TextDecoration == CssConstants.LineThrough)
            {
                y = rectangle.Top + rectangle.Height / 2f;
            }
            else if (TextDecoration == CssConstants.Overline)
            {
                y = rectangle.Top;
            }
            y -= ActualPaddingBottom - ActualBorderBottomWidth;

            double x1 = rectangle.X;
            if (isFirst)
                x1 += ActualPaddingLeft + ActualBorderLeftWidth;

            double x2 = rectangle.Right;
            if (isLast)
                x2 -= ActualPaddingRight + ActualBorderRightWidth;

            var pen = g.GetPen(ActualColor);
            pen.Width = 1;
            pen.DashStyle = RDashStyle.Solid;
            g.DrawLine(pen, x1, y, x2, y);
        }
Example #19
0
        /// <summary>
        /// Paint all the words in the box.
        /// </summary>
        /// <param name="g">the device to draw into</param>
        /// <param name="offset">the current scroll offset to offset the words</param>
        private void PaintWords(RGraphics g, RPoint offset)
        {
            if (Width.Length > 0)
            {
                var isRtl = Direction == CssConstants.Rtl;
                foreach (var word in Words)
                {
                    if (!word.IsLineBreak)
                    {
                        var wordPoint = new RPoint(word.Left + offset.X, word.Top + offset.Y);
                        if (word.Selected)
                        {
                            // handle paint selected word background and with partial word selection
                            var wordLine = DomUtils.GetCssLineBoxByWord(word);
                            var left = word.SelectedStartOffset > -1 ? word.SelectedStartOffset : (wordLine.Words[0] != word && word.HasSpaceBefore ? -ActualWordSpacing : 0);
                            var padWordRight = word.HasSpaceAfter && !wordLine.IsLastSelectedWord(word);
                            var width = word.SelectedEndOffset > -1 ? word.SelectedEndOffset : word.Width + (padWordRight ? ActualWordSpacing : 0);
                            var rect = new RRect(word.Left + offset.X + left, word.Top + offset.Y, width - left, wordLine.LineHeight);

                            g.DrawRectangle(GetSelectionBackBrush(g, false), rect.X, rect.Y, rect.Width, rect.Height);

                            if (HtmlContainer.SelectionForeColor != RColor.Empty && (word.SelectedStartOffset > 0 || word.SelectedEndIndexOffset > -1))
                            {
                                g.PushClipExclude(rect);
                                g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
                                g.PopClip();
                                g.PushClip(rect);
                                g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
                                g.PopClip();
                            }
                            else
                            {
                                g.DrawString(word.Text, ActualFont, GetSelectionForeBrush(), wordPoint, new RSize(word.Width, word.Height), isRtl);
                            }
                        }
                        else
                        {
                            //                            g.DrawRectangle(HtmlContainer.Adapter.GetPen(RColor.Black), wordPoint.X, wordPoint.Y, word.Width - 1, word.Height - 1);
                            g.DrawString(word.Text, ActualFont, ActualColor, wordPoint, new RSize(word.Width, word.Height), isRtl);
                        }
                    }
                }
            }
        }
Example #20
0
        /// <summary>
        /// Paints the background of the box
        /// </summary>
        /// <param name="g">the device to draw into</param>
        /// <param name="rect">the bounding rectangle to draw in</param>
        /// <param name="isFirst">is it the first rectangle of the element</param>
        /// <param name="isLast">is it the last rectangle of the element</param>
        protected void PaintBackground(RGraphics g, RRect rect, bool isFirst, bool isLast)
        {
            if (rect.Width > 0 && rect.Height > 0)
            {
                RBrush brush = null;

                if (BackgroundGradient != CssConstants.None)
                {
                    brush = g.GetLinearGradientBrush(rect, ActualBackgroundColor, ActualBackgroundGradient, ActualBackgroundGradientAngle);
                }
                else if (RenderUtils.IsColorVisible(ActualBackgroundColor))
                {
                    brush = g.GetSolidBrush(ActualBackgroundColor);
                }

                if (brush != null)
                {
                    // TODO:a handle it correctly (tables background)
                    // if (isLast)
                    //  rectangle.Width -= ActualWordSpacing + CssUtils.GetWordEndWhitespace(ActualFont);

                    RGraphicsPath roundrect = null;
                    if (IsRounded)
                    {
                        roundrect = RenderUtils.GetRoundRect(g, rect, ActualCornerNw, ActualCornerNe, ActualCornerSe, ActualCornerSw);
                    }

                    Object prevMode = null;
                    if (HtmlContainer != null && !HtmlContainer.AvoidGeometryAntialias && IsRounded)
                    {
                        prevMode = g.SetAntiAliasSmoothingMode();
                    }

                    if (roundrect != null)
                    {
                        g.DrawPath(brush, roundrect);
                    }
                    else
                    {
                        g.DrawRectangle(brush, Math.Ceiling(rect.X), Math.Ceiling(rect.Y), rect.Width, rect.Height);
                    }

                    g.ReturnPreviousSmoothingMode(prevMode);

                    if (roundrect != null)
                        roundrect.Dispose();
                    brush.Dispose();
                }

                if (_imageLoadHandler != null && _imageLoadHandler.Image != null && isFirst)
                {
                    BackgroundImageDrawHandler.DrawBackgroundImage(g, this, _imageLoadHandler, rect);
                }
            }
        }
 /// <summary>
 /// Draw simple border.
 /// </summary>
 /// <param name="border">Desired border</param>
 /// <param name="g">the device to draw to</param>
 /// <param name="box">Box which the border corresponds</param>
 /// <param name="brush">the brush to use</param>
 /// <param name="rectangle">the bounding rectangle to draw in</param>
 /// <returns>Beveled border path, null if there is no rounded corners</returns>
 public static void DrawBorder(Border border, RGraphics g, CssBox box, RBrush brush, RRect rectangle)
 {
     SetInOutsetRectanglePoints(border, box, rectangle, true, true);
     g.DrawPolygon(brush, _borderPts);
 }
Example #22
0
        /// <summary>
        /// Assigns words its width and height
        /// </summary>
        /// <param name="g"></param>
        internal virtual void MeasureWordsSize(RGraphics g)
        {
            if (!_wordsSizeMeasured)
            {
                if (BackgroundImage != CssConstants.None && _imageLoadHandler == null)
                {
                    _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnImageLoadComplete);
                    _imageLoadHandler.LoadImage(BackgroundImage, HtmlTag != null ? HtmlTag.Attributes : null);
                }

                MeasureWordSpacing(g);

                if (Words.Count > 0)
                {
                    foreach (var boxWord in Words)
                    {
                        boxWord.Width = boxWord.Text != "\n" ? g.MeasureString(boxWord.Text, ActualFont).Width : 0;
                        boxWord.Height = ActualFont.Height;
                    }
                }

                _wordsSizeMeasured = true;
            }
        }
        /// <summary>
        /// Analyzes the Table and assigns values to this CssTable object.
        /// To be called from the constructor
        /// </summary>
        private void Layout(RGraphics g)
        {
            MeasureWords(_tableBox, g);

            // get the table boxes into the proper fields
            AssignBoxKinds();

            // Insert EmptyBoxes for vertical cell spanning.
            InsertEmptyBoxes();

            // Determine Row and Column Count, and ColumnWidths
            var availCellSpace = CalculateCountAndWidth();

            DetermineMissingColumnWidths(availCellSpace);

            // Check for minimum sizes (increment widths if necessary)
            EnforceMinimumSize();

            // While table width is larger than it should, and width is reducible
            EnforceMaximumSize();

            // Ensure there's no padding
            _tableBox.PaddingLeft = _tableBox.PaddingTop = _tableBox.PaddingRight = _tableBox.PaddingBottom = "0";

            //Actually layout cells!
            LayoutCells(g);
        }
Example #24
0
        /// <summary>
        /// Creates the <see cref="_listItemBox"/>
        /// </summary>
        /// <param name="g"></param>
        private void CreateListItemBox(RGraphics g)
        {
            if (Display == CssConstants.ListItem && ListStyleType != CssConstants.None)
            {
                if (_listItemBox == null)
                {
                    _listItemBox = new CssBox(null, null);
                    _listItemBox.InheritStyle(this);
                    _listItemBox.Display = CssConstants.Inline;
                    _listItemBox.HtmlContainer = HtmlContainer;

                    if (ListStyleType.Equals(CssConstants.Disc, StringComparison.InvariantCultureIgnoreCase))
                    {
                        _listItemBox.Text = new SubString("•");
                    }
                    else if (ListStyleType.Equals(CssConstants.Circle, StringComparison.InvariantCultureIgnoreCase))
                    {
                        _listItemBox.Text = new SubString("o");
                    }
                    else if (ListStyleType.Equals(CssConstants.Square, StringComparison.InvariantCultureIgnoreCase))
                    {
                        _listItemBox.Text = new SubString("♠");
                    }
                    else if (ListStyleType.Equals(CssConstants.Decimal, StringComparison.InvariantCultureIgnoreCase))
                    {
                        _listItemBox.Text = new SubString(GetIndexForList().ToString(CultureInfo.InvariantCulture) + ".");
                    }
                    else if (ListStyleType.Equals(CssConstants.DecimalLeadingZero, StringComparison.InvariantCultureIgnoreCase))
                    {
                        _listItemBox.Text = new SubString(GetIndexForList().ToString("00", CultureInfo.InvariantCulture) + ".");
                    }
                    else
                    {
                        _listItemBox.Text = new SubString(CommonUtils.ConvertToAlphaNumber(GetIndexForList(), ListStyleType) + ".");
                    }

                    _listItemBox.ParseToWords();

                    _listItemBox.PerformLayoutImp(g);
                    _listItemBox.Size = new RSize(_listItemBox.Words[0].Width, _listItemBox.Words[0].Height);
                }
                _listItemBox.Words[0].Left = Location.X - _listItemBox.Size.Width - 5;
                _listItemBox.Words[0].Top = Location.Y + ActualPaddingTop; // +FontAscent;
            }
        }
        /// <summary>
        /// Draw the background image at the required location repeating it over the X and Y axis.<br/>
        /// Adjust location to left-top if starting location doesn't include all the range (adjusted to center or bottom/right).
        /// </summary>
        private static void DrawRepeat(RGraphics g, ImageLoadHandler imageLoadHandler, RRect rectangle, RRect srcRect, RRect destRect, RSize imgSize)
        {
            while (destRect.X > rectangle.X)
                destRect.X -= imgSize.Width;
            while (destRect.Y > rectangle.Y)
                destRect.Y -= imgSize.Height;

            using (var brush = g.GetTextureBrush(imageLoadHandler.Image, srcRect, destRect.Location))
            {
                g.DrawRectangle(brush, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="g"></param>
        /// <param name="tableBox"> </param>
        public static void PerformLayout(RGraphics g, CssBox tableBox)
        {
            ArgChecker.AssertArgNotNull(g, "g");
            ArgChecker.AssertArgNotNull(tableBox, "tableBox");

            try
            {
                var table = new CssLayoutEngineTable(tableBox);
                table.Layout(g);
            }
            catch (Exception ex)
            {
                tableBox.HtmlContainer.ReportError(HtmlRenderErrorType.Layout, "Failed table layout", ex);
            }
        }
Example #27
0
        /// <summary>
        /// Assigns words its width and height
        /// </summary>
        /// <param name="g">the device to use</param>
        internal override void MeasureWordsSize(RGraphics g)
        {
            if (!_wordsSizeMeasured)
            {
                if (_imageLoadHandler == null && (HtmlContainer.AvoidAsyncImagesLoading || HtmlContainer.AvoidImagesLateLoading))
                {
                    _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnLoadImageComplete);
                    _imageLoadHandler.LoadImage(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null);
                }

                MeasureWordSpacing(g);
                _wordsSizeMeasured = true;
            }

            CssLayoutEngine.MeasureImageSize(_imageWord);
        }
        /// <summary>
        /// Layout the cells by the calculated table layout
        /// </summary>
        /// <param name="g"></param>
        private void LayoutCells(RGraphics g)
        {
            double startx = Math.Max(_tableBox.ClientLeft + GetHorizontalSpacing(), 0);
            double starty = Math.Max(_tableBox.ClientTop + GetVerticalSpacing(), 0);
            double cury = starty;
            double maxRight = startx;
            double maxBottom = 0f;
            int currentrow = 0;

            // change start X by if the table should align to center or right
            if (_tableBox.TextAlign == CssConstants.Center || _tableBox.TextAlign == CssConstants.Right)
            {
                double maxRightCalc = GetWidthSum();
                startx = _tableBox.TextAlign == CssConstants.Right
                    ? GetAvailableTableWidth() - maxRightCalc
                    : startx + (GetAvailableTableWidth() - maxRightCalc) / 2;

                _tableBox.Location = new RPoint(startx - _tableBox.ActualBorderLeftWidth - _tableBox.ActualPaddingLeft - GetHorizontalSpacing(), _tableBox.Location.Y);
            }

            for (int i = 0; i < _allRows.Count; i++)
            {
                var row = _allRows[i];
                double curx = startx;
                int curCol = 0;

                for (int j = 0; j < row.Boxes.Count; j++)
                {
                    CssBox cell = row.Boxes[j];
                    if (curCol >= _columnWidths.Length)
                        break;

                    int rowspan = GetRowSpan(cell);
                    var columnIndex = GetCellRealColumnIndex(row, cell);
                    double width = GetCellWidth(columnIndex, cell);
                    cell.Location = new RPoint(curx, cury);
                    cell.Size = new RSize(width, 0f);
                    cell.PerformLayout(g); //That will automatically set the bottom of the cell

                    //Alter max bottom only if row is cell's row + cell's rowspan - 1
                    CssSpacingBox sb = cell as CssSpacingBox;
                    if (sb != null)
                    {
                        if (sb.EndRow == currentrow)
                        {
                            maxBottom = Math.Max(maxBottom, sb.ExtendedBox.ActualBottom);
                        }
                    }
                    else if (rowspan == 1)
                    {
                        maxBottom = Math.Max(maxBottom, cell.ActualBottom);
                    }
                    maxRight = Math.Max(maxRight, cell.ActualRight);
                    curCol++;
                    curx = cell.ActualRight + GetHorizontalSpacing();
                }

                foreach (CssBox cell in row.Boxes)
                {
                    CssSpacingBox spacer = cell as CssSpacingBox;

                    if (spacer == null && GetRowSpan(cell) == 1)
                    {
                        cell.ActualBottom = maxBottom;
                        CssLayoutEngine.ApplyCellVerticalAlignment(g, cell);
                    }
                    else if (spacer != null && spacer.EndRow == currentrow)
                    {
                        spacer.ExtendedBox.ActualBottom = maxBottom;
                        CssLayoutEngine.ApplyCellVerticalAlignment(g, spacer.ExtendedBox);
                    }
                }

                cury = maxBottom + GetVerticalSpacing();
                currentrow++;
            }

            maxRight = Math.Max(maxRight, _tableBox.Location.X + _tableBox.ActualWidth);
            _tableBox.ActualRight = maxRight + GetHorizontalSpacing() + _tableBox.ActualBorderRightWidth;
            _tableBox.ActualBottom = Math.Max(maxBottom, starty) + GetVerticalSpacing() + _tableBox.ActualBorderBottomWidth;
        }
Example #29
0
        /// <summary>
        /// Sets the baseline of the words of the specified box to certain height
        /// </summary>
        /// <param name="g">Device info</param>
        /// <param name="b">box to check words</param>
        /// <param name="baseline">baseline</param>
        internal void SetBaseLine(RGraphics g, CssBox b, double baseline)
        {
            //TODO: Aqui me quede, checar poniendo "by the" con un font-size de 3em
            List<CssRect> ws = WordsOf(b);

            if (!Rectangles.ContainsKey(b))
                return;

            RRect r = Rectangles[b];

            //Save top of words related to the top of rectangle
            double gap = 0f;

            if (ws.Count > 0)
            {
                gap = ws[0].Top - r.Top;
            }
            else
            {
                CssRect firstw = b.FirstWordOccourence(b, this);

                if (firstw != null)
                {
                    gap = firstw.Top - r.Top;
                }
            }

            //New top that words will have
            //float newtop = baseline - (Height - OwnerBox.FontDescent - 3); //OLD
            double newtop = baseline; // -GetBaseLineHeight(b, g); //OLD

            if (b.ParentBox != null &&
                b.ParentBox.Rectangles.ContainsKey(this) &&
                r.Height < b.ParentBox.Rectangles[this].Height)
            {
                //Do this only if rectangle is shorter than parent's
                double recttop = newtop - gap;
                RRect newr = new RRect(r.X, recttop, r.Width, r.Height);
                Rectangles[b] = newr;
                b.OffsetRectangle(this, gap);
            }

            foreach (var word in ws)
            {
                if (!word.IsImage)
                    word.Top = newtop;
            }
        }
 /// <summary>
 /// Recursively measures words inside the box
 /// </summary>
 /// <param name="box">the box to measure</param>
 /// <param name="g">Device to use</param>
 private static void MeasureWords(CssBox box, RGraphics g)
 {
     if (box != null)
     {
         foreach (var childBox in box.Boxes)
         {
             childBox.MeasureWordsSize(g);
             MeasureWords(childBox, g);
         }
     }
 }