/// <summary> /// Applies vertical and horizontal alignment to words in lineboxes /// </summary> /// <param name="g"></param> /// <param name="lineBox"></param> private static void ApplyHorizontalAlignment(RGraphics g, CssLineBox lineBox) { switch (lineBox.OwnerBox.TextAlign) { case CssConstants.Right: ApplyRightAlignment(g, lineBox); break; case CssConstants.Center: ApplyCenterAlignment(g, lineBox); break; case CssConstants.Justify: ApplyJustifyAlignment(g, lineBox); break; default: ApplyLeftAlignment(g, lineBox); break; } }
/// <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> /// Measures the bounds of box and children, recursively. /// </summary> /// <param name="g">Device context to draw</param> public void PerformLayout(RGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); _actualSize = RSize.Empty; if (_root != null) { // if width is not restricted we set it to large value to get the actual later _root.Size = new RSize(_maxSize.Width > 0 ? _maxSize.Width : 99999, 0); _root.Location = _location; _root.PerformLayout(g); if (_maxSize.Width <= 0.1) { // in case the width is not restricted we need to double layout, first will find the width so second can layout by it (center alignment) _root.Size = new RSize((int)Math.Ceiling(_actualSize.Width), 0); _actualSize = RSize.Empty; _root.PerformLayout(g); } } }
/// <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); } }
/// <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> /// Render the html using the given device. /// </summary> /// <param name="g">the device to use to render</param> public void PerformPaint(RGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); bool pushedClip = false; if (MaxSize.Height > 0) { pushedClip = true; g.PushClip(new RRect(_location, _maxSize)); } if (_root != null) { _root.Paint(g); } if (pushedClip) { g.PopClip(); } }
/// <summary> /// Applies centered alignment to the text on the linebox /// </summary> /// <param name="g"></param> /// <param name="lineBox"></param> private static void ApplyJustifyAlignment(RGraphics g, CssLineBox lineBox) { if (lineBox.Equals(lineBox.OwnerBox.LineBoxes[lineBox.OwnerBox.LineBoxes.Count - 1])) { return; } double indent = lineBox.Equals(lineBox.OwnerBox.LineBoxes[0]) ? lineBox.OwnerBox.ActualTextIndent : 0f; double textSum = 0f; double words = 0f; double availWidth = lineBox.OwnerBox.ClientRectangle.Width - indent; // Gather text sum foreach (CssRect w in lineBox.Words) { textSum += w.Width; words += 1f; } if (words <= 0f) { return; //Avoid Zero division } double spacing = (availWidth - textSum) / words; //Spacing that will be used double curx = lineBox.OwnerBox.ClientLeft + indent; foreach (CssRect word in lineBox.Words) { word.Left = curx; curx = word.Right + spacing; if (word == lineBox.Words[lineBox.Words.Count - 1]) { word.Left = lineBox.OwnerBox.ClientRight - word.Width; } } }
private static void LayoutCells(RGraphics g, Table table) { double top = table.tableBox.Location.Y; double left = table.tableBox.Location.X; for (int row = 0; row < table.rows; row++) { left = table.tableBox.Location.X; for (int col = 0; col < table.cols; col++) { if (table[row, col] != null) { var td = table[row, col]; int colspan = GetColSpan(td); int rowspan = GetRowSpan(td); double height = 0; for (int xr = 0; xr < rowspan; xr++) { height += table.rows_limits[row + xr].value; } double width = 0; for (int xc = 0; xc < colspan; xc++) { width += table.cols_limits[col + xc].value; } td.PerformLayout(g); td.Location = new RPoint(left, top); td.Size = new RSize(width, height); //That will automatically set the bottom of the cell } left += table.cols_limits[col].value; } top += table.rows_limits[row].value; } }
/// <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); if (this.Content != null && this.Content != CssConstants.Normal) { _imageLoadHandler.LoadImage(this.Content, HtmlTag != null ? HtmlTag.Attributes : null); } else { _imageLoadHandler.LoadImage(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null); } } MeasureWordSpacing(g); _wordsSizeMeasured = true; } CssLayoutEngine.MeasureImageSize(_imageWord); }
/// <summary> /// Creates a rounded rectangle using the specified corner radius<br/> /// NW-----NE /// | | /// | | /// SW-----SE /// </summary> /// <param name="g">the device to draw into</param> /// <param name="rect">Rectangle to round</param> /// <param name="nwRadius">Radius of the north east corner</param> /// <param name="neRadius">Radius of the north west corner</param> /// <param name="seRadius">Radius of the south east corner</param> /// <param name="swRadius">Radius of the south west corner</param> /// <returns>GraphicsPath with the lines of the rounded rectangle ready to be painted</returns> public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect, double nwRadius, double neRadius, double seRadius, double swRadius) { var path = g.GetGraphicsPath(); path.Start(rect.Left + nwRadius, rect.Top); path.LineTo(rect.Right - neRadius, rect.Y); if (neRadius > 0f) { path.ArcTo(rect.Right, rect.Top + neRadius, neRadius, RGraphicsPath.Corner.TopRight); } path.LineTo(rect.Right, rect.Bottom - seRadius); if (seRadius > 0f) { path.ArcTo(rect.Right - seRadius, rect.Bottom, seRadius, RGraphicsPath.Corner.BottomRight); } path.LineTo(rect.Left + swRadius, rect.Bottom); if (swRadius > 0f) { path.ArcTo(rect.Left, rect.Bottom - swRadius, swRadius, RGraphicsPath.Corner.BottomLeft); } path.LineTo(rect.Left, rect.Top + nwRadius); if (nwRadius > 0f) { path.ArcTo(rect.Left + nwRadius, rect.Top, nwRadius, RGraphicsPath.Corner.TopLeft); } return(path); }
/// <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(); var noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden; var 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(); var noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden; var 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); }
/// <summary> /// Paints the fragment /// </summary> /// <param name="g">the device to draw to</param> protected override void PaintImp(RGraphics g) { // load image if 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 = RPoint.Empty; if (!IsFixed) { 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(); } }
/// <summary> /// Recursively flows the content of the box using the inline model /// </summary> /// <param name="g">Device Info</param> /// <param name="blockbox">Blockbox that contains the text flow</param> /// <param name="box">Current box to flow its content</param> /// <param name="limitRight">Maximum reached right</param> /// <param name="linespacing">Space to use between rows of text</param> /// <param name="startx">x starting coordinate for when breaking lines of text</param> /// <param name="line">Current linebox being used</param> /// <param name="curx">Current x coordinate that will be the left of the next word</param> /// <param name="cury">Current y coordinate that will be the top of the next word</param> /// <param name="maxRight">Maximum right reached so far</param> /// <param name="maxbottom">Maximum bottom reached so far</param> private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double limitRight, double linespacing, double startx, ref CssLineBox line, ref double curx, ref double cury, ref double maxRight, ref double maxbottom) { var startX = curx; var startY = cury; box.FirstHostingLineBox = line; var localCurx = curx; var localMaxRight = maxRight; var localmaxbottom = maxbottom; foreach (CssBox b in box.Boxes) { double leftspacing = (b.Position != CssConstants.Absolute && b.Position != CssConstants.Fixed) ? b.ActualMarginLeft + b.ActualBorderLeftWidth + b.ActualPaddingLeft : 0; double rightspacing = (b.Position != CssConstants.Absolute && b.Position != CssConstants.Fixed) ? b.ActualMarginRight + b.ActualBorderRightWidth + b.ActualPaddingRight : 0; b.RectanglesReset(); b.MeasureWordsSize(g); curx += leftspacing; if (b.Words.Count > 0) { bool wrapNoWrapBox = false; if (b.WhiteSpace == CssConstants.NoWrap && curx > startx) { var boxRight = curx; foreach (var word in b.Words) { boxRight += word.FullWidth; } if (boxRight > limitRight) { wrapNoWrapBox = true; } } if (DomUtils.IsBoxHasWhitespace(b)) { curx += box.ActualWordSpacing; } foreach (var word in b.Words) { if (maxbottom - cury < box.ActualLineHeight) { maxbottom += box.ActualLineHeight - (maxbottom - cury); } if ((b.WhiteSpace != CssConstants.NoWrap && b.WhiteSpace != CssConstants.Pre && curx + word.Width + rightspacing > limitRight && (b.WhiteSpace != CssConstants.PreWrap || !word.IsSpaces)) || word.IsLineBreak || wrapNoWrapBox) { wrapNoWrapBox = false; curx = startx; // handle if line is wrapped for the first text element where parent has left margin\padding if (b == box.Boxes[0] && !word.IsLineBreak && (word == b.Words[0] || (box.ParentBox != null && box.ParentBox.IsBlock))) { curx += box.ActualMarginLeft + box.ActualBorderLeftWidth + box.ActualPaddingLeft; } cury = maxbottom + linespacing; line = new CssLineBox(blockbox); if (word.IsImage || word.Equals(b.FirstWord)) { curx += leftspacing; } } line.ReportExistanceOf(word); word.Left = curx; word.Top = cury; if (!box.IsFixed) { word.BreakPage(); } curx = word.Left + word.FullWidth; maxRight = Math.Max(maxRight, word.Right); maxbottom = Math.Max(maxbottom, word.Bottom); if (b.Position == CssConstants.Absolute) { word.Left += box.ActualMarginLeft; word.Top += box.ActualMarginTop; } } } else { FlowBox(g, blockbox, b, limitRight, linespacing, startx, ref line, ref curx, ref cury, ref maxRight, ref maxbottom); } curx += rightspacing; } // handle height setting if (maxbottom - startY < box.ActualHeight) { maxbottom += box.ActualHeight - (maxbottom - startY); } // handle width setting if (box.IsInline && 0 <= curx - startX && curx - startX < box.ActualWidth) { // hack for actual width handling curx += box.ActualWidth - (curx - startX); line.Rectangles.Add(box, new RRect(startX, startY, box.ActualWidth, box.ActualHeight)); } // handle box that is only a whitespace if (box.Text != null && box.Text.IsWhitespace() && !box.IsImage && box.IsInline && box.Boxes.Count == 0 && box.Words.Count == 0) { curx += box.ActualWordSpacing; } // hack to support specific absolute position elements if (box.Position == CssConstants.Absolute) { curx = localCurx; maxRight = localMaxRight; maxbottom = localmaxbottom; AdjustAbsolutePosition(box, 0, 0); } box.LastHostingLineBox = line; }
/// <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; 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; }
public abstract double GetWhitespaceWidth(RGraphics graphics);
/// <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); }
/// <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 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, RGraphicsPath roundrect = null) { // 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, box); 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); //rectangle - box to draw in //imgSize - image size //localion - inital location of image wo repeat //destRect - RRect(location, imgSize) //srcRect (0,0,imgSize) //brushRect - This is rectangle which needs to be filled with Image from brushRect.Location with brushRect.Size multiple to Image size. RRect brushRect = new RRect(location, imgSize); switch (box.BackgroundRepeat) { case "no-repeat": //brushRect = destRect; break; case "repeat-x": if (brushRect.X > rectangle.X) { brushRect.X -= imgSize.Width * ((int)((brushRect.X - rectangle.X) / imgSize.Width) + 1); } if (brushRect.X + brushRect.Width < rectangle.X + rectangle.Width) { brushRect.Width = imgSize.Width * ((int)((rectangle.X + rectangle.Width - brushRect.X) / imgSize.Width) + 1); } break; case "repeat-y": if (brushRect.Y > rectangle.Y) { brushRect.Y -= imgSize.Height * ((int)((brushRect.Y - rectangle.Y) / imgSize.Height) + 1); } if (brushRect.Y + brushRect.Height < rectangle.Y + rectangle.Height) { brushRect.Height = imgSize.Height * ((int)((rectangle.Y + rectangle.Height - brushRect.Y) / imgSize.Height) + 1); } break; default: if (brushRect.X > rectangle.X) { brushRect.X -= imgSize.Width * ((int)((brushRect.X - rectangle.X) / imgSize.Width) + 1); } if (brushRect.X + brushRect.Width < rectangle.X + rectangle.Width) { brushRect.Width = imgSize.Width * ((int)((rectangle.X + rectangle.Width - brushRect.X) / imgSize.Width) + 1); } if (brushRect.Y > rectangle.Y) { brushRect.Y -= imgSize.Height * ((int)((brushRect.Y - rectangle.Y) / imgSize.Height) + 1); } if (brushRect.Y + brushRect.Height < rectangle.Y + rectangle.Height) { brushRect.Height = imgSize.Height * ((int)((rectangle.Y + rectangle.Height - brushRect.Y) / imgSize.Height) + 1); } break; } using (var brush = g.GetTextureBrush(imageLoadHandler.Image, brushRect, brushRect.Location)) { if (roundrect == null) { g.DrawRectangle(brush, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } else { g.DrawPath(brush, roundrect); } } g.PopClip(); }
/// <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; bool breakPage = false; 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); } // If one cell crosses page borders then don't need to check other cells in the row if (_tableBox.PageBreakInside == CssConstants.Avoid) { breakPage = cell.BreakPage(); if (breakPage) { cury = cell.Location.Y; break; } } } if (breakPage) // go back to move the whole row to the next page { if (i == 1) // do not leave single row in previous page { i = -1; // Start layout from the first row on new page } else { i--; } maxBottom = 0; continue; } 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; }
/// <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; } } } }