/// <summary> /// Initializes a new instance of the <see cref="RRect" /> class with the specified location and size. /// </summary> /// <param name="location">A <see cref="RPoint" /> that represents the upper-left corner of the rectangular region.</param> /// <param name="size">A <see cref="RSize" /> that represents the width and height of the rectangular region.</param> public RRect(RPoint location, RSize size) { _x = location.X; _y = location.Y; _width = size.Width; _height = size.Height; }
/// <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> /// Get top-left location to start drawing the image at depending on background-position value. /// </summary> /// <param name="backgroundPosition">the background-position value</param> /// <param name="rectangle">the rectangle to position image in</param> /// <param name="imgSize">the size of the image</param> /// <returns>the top-left location</returns> private static RPoint GetLocation(string backgroundPosition, RRect rectangle, RSize imgSize) { double left = rectangle.Left; if (backgroundPosition.IndexOf("left", StringComparison.OrdinalIgnoreCase) > -1) { left = (rectangle.Left + .5f); } else if (backgroundPosition.IndexOf("right", StringComparison.OrdinalIgnoreCase) > -1) { left = rectangle.Right - imgSize.Width; } else if (backgroundPosition.IndexOf("0", StringComparison.OrdinalIgnoreCase) < 0) { left = (rectangle.Left + (rectangle.Width - imgSize.Width) / 2 + .5f); } double top = rectangle.Top; if (backgroundPosition.IndexOf("top", StringComparison.OrdinalIgnoreCase) > -1) { top = rectangle.Top; } else if (backgroundPosition.IndexOf("bottom", StringComparison.OrdinalIgnoreCase) > -1) { top = rectangle.Bottom - imgSize.Height; } else if (backgroundPosition.IndexOf("0", StringComparison.OrdinalIgnoreCase) < 0) { top = (rectangle.Top + (rectangle.Height - imgSize.Height) / 2 + .5f); } return new RPoint(left, top); }
/// <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> /// Translates a <see cref="RPoint" /> by the negative of a specified size. /// </summary> /// <returns> /// The translated <see cref="RPoint" />. /// </returns> /// <param name="pt"> /// The <see cref="RPoint" /> to translate. /// </param> /// <param name="sz"> /// The <see cref="T:System.Drawing.SizeF" /> that specifies the numbers to subtract from the coordinates of /// <paramref /// name="pt" /> /// . /// </param> public static RPoint Subtract(RPoint pt, RSize sz) { return new RPoint(pt.X - sz.Width, pt.Y - sz.Height); }
/// <summary> /// Translates a given <see cref="RPoint" /> by a specified /// <see /// cref="T:System.Drawing.SizeF" /> /// . /// </summary> /// <returns> /// The translated <see cref="RPoint" />. /// </returns> /// <param name="pt"> /// The <see cref="RPoint" /> to translate. /// </param> /// <param name="sz"> /// The <see cref="T:System.Drawing.SizeF" /> that specifies the numbers to add to the coordinates of /// <paramref /// name="pt" /> /// . /// </param> public static RPoint Add(RPoint pt, RSize sz) { return new RPoint(pt.X + sz.Width, pt.Y + sz.Height); }
/// <summary> /// Inflates this <see cref="RRect" /> by the specified amount. /// </summary> /// <param name="size">The amount to inflate this rectangle. </param> public void Inflate(RSize size) { Inflate(size.Width, size.Height); }
/// <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> /// 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> /// Draw the given string using the given font and foreground color at given location. /// </summary> /// <param name="str">the string to draw</param> /// <param name="font">the font to use to draw the string</param> /// <param name="color">the text color to set</param> /// <param name="point">the location to start string draw (top-left)</param> /// <param name="size">used to know the size of the rendered text for transparent text support</param> /// <param name="rtl">is to render the string right-to-left (true - RTL, false - LTR)</param> public abstract void DrawString(String str, RFont font, RColor color, RPoint point, RSize size, bool rtl);
/// <summary> /// Initializes a new instance of the <see cref="RSize" /> structure from the specified existing /// <see /// cref="RSize" /> /// structure. /// </summary> /// <param name="size"> /// The <see cref="RSize" /> structure from which to create the new /// <see /// cref="RSize" /> /// structure. /// </param> public RSize(RSize size) { _width = size._width; _height = size._height; }
/// <summary> /// Subtracts the width and height of one <see cref="RSize" /> structure from the width and height of another /// <see /// cref="RSize" /> /// structure. /// </summary> /// <returns> /// A <see cref="RSize" /> structure that is a result of the subtraction operation. /// </returns> /// <param name="sz1"> /// The <see cref="RSize" /> structure on the left side of the subtraction operator. /// </param> /// <param name="sz2"> /// The <see cref="RSize" /> structure on the right side of the subtraction operator. /// </param> public static RSize Subtract(RSize sz1, RSize sz2) { return new RSize(sz1.Width - sz2.Width, sz1.Height - sz2.Height); }
/// <summary> /// Adds the width and height of one <see cref="RSize" /> structure to the width and height of another /// <see /// cref="RSize" /> /// structure. /// </summary> /// <returns> /// A <see cref="RSize" /> structure that is the result of the addition operation. /// </returns> /// <param name="sz1"> /// The first <see cref="RSize" /> structure to add. /// </param> /// <param name="sz2"> /// The second <see cref="RSize" /> structure to add. /// </param> public static RSize Add(RSize sz1, RSize sz2) { return new RSize(sz1.Width + sz2.Width, sz1.Height + sz2.Height); }
/// <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)); }
/// <summary> /// Draw play over the iframe if we found link url. /// </summary> private void DrawPlay(RGraphics g, RRect rect) { if (_isVideo && _imageWord.Width > 70 && _imageWord.Height > 50) { var prevMode = g.SetAntiAliasSmoothingMode(); var size = new RSize(60, 40); var left = rect.Left + (rect.Width - size.Width) / 2; var top = rect.Top + (rect.Height - size.Height) / 2; g.DrawRectangle(g.GetSolidBrush(RColor.FromArgb(160, 0, 0, 0)), left, top, size.Width, size.Height); RPoint[] points = { new RPoint(left + size.Width / 3f + 1,top + 3 * size.Height / 4f), new RPoint(left + size.Width / 3f + 1, top + size.Height / 4f), new RPoint(left + 2 * size.Width / 3f + 1, top + size.Height / 2f) }; g.DrawPolygon(g.GetSolidBrush(RColor.White), points); g.ReturnPreviousSmoothingMode(prevMode); } }