// "Therefore those skilled at the unorthodox // are infinite as heaven and earth, // inexhaustible as the great rivers. // When they come to an end, // they begin again, // like the days and months; // they die and are reborn, // like the four seasons." // // - Sun Tsu, // "The Art of War" using System; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Handlers; using TheArtOfDev.HtmlRenderer.Core.Utils; namespace TheArtOfDev.HtmlRenderer.Core.Dom { /// /// CSS box for image element. /// internal sealed class CssBoxImage : CssBox { #region Fields and Consts /// /// the image word of this image box /// private readonly CssRectImage _imageWord; /// /// handler used for image loading by source /// private ImageLoadHandler _imageLoadHandler; /// /// is image load is finished, used to know if no image is found /// private bool _imageLoadingComplete; #endregion /// /// Init. /// /// the parent box of this box /// the html tag data of this box public CssBoxImage(CssBox parent, HtmlTag tag) : base(parent, tag) { _imageWord = new CssRectImage(this); Words.Add(_imageWord); } /// /// Get the image of this image box. /// public RImage Image { get { return _imageWord.Image; } } /// /// Paints the fragment /// /// the device to draw to 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(); } /// /// Assigns words its width and height /// /// the device to use 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); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public override void Dispose() { if (_imageLoadHandler != null) _imageLoadHandler.Dispose(); base.Dispose(); } #region Private methods /// /// Set error image border on the image box. /// private void SetErrorBorder() { SetAllBorders(CssConstants.Solid, "2px", "#A0A0A0"); BorderRightColor = BorderBottomColor = "#E3E3E3"; } /// /// On image load process is complete with image or without update the image box. /// /// the image loaded or null if failed /// the source rectangle to draw in the image (empty - draw everything) /// is the callback was called async to load image call private void OnLoadImageComplete(RImage image, RRect rectangle, bool async) { _imageWord.Image = image; _imageWord.ImageRectangle = rectangle; _imageLoadingComplete = true; _wordsSizeMeasured = false; if (_imageLoadingComplete && image == null) { SetErrorBorder(); } if (!HtmlContainer.AvoidImagesLateLoading || async) { var width = new CssLength(Width); var height = new CssLength(Height); var layout = (width.Number <= 0 || width.Unit != CssUnit.Pixels) || (height.Number <= 0 || height.Unit != CssUnit.Pixels); HtmlContainer.RequestRefresh(layout); } } #endregion } }