locked
How to convert webview source (html string) to image format RRS feed

  • Question

  • User277514 posted

    I am able to display html string as webview source but I want to download the webview as an image in xamarin forms. Please help

    Sunday, November 17, 2019 5:09 AM

All replies

  • User277514 posted

    var browser = new WebView(); var htmlSource = new HtmlWebViewSource(); htmlSource.Html = @"

    Xamarin.Forms

    Card

    Vivek

    Xamarin Developer

    Welcome to WebView.

    ";

            webviews.Source = htmlSource;
    
    Sunday, November 17, 2019 5:10 AM
  • User369979 posted

    Try to make a custom renderer for webview on each platform. on iOS:

    [assembly: ExportRenderer(typeof(WebView), typeof(CustomWebViewRenderer))]
    namespace App.iOS
    {
        public class CustomWebViewRenderer : WebViewRenderer
        {
    
            protected override void OnElementChanged(VisualElementChangedEventArgs e)
            {
                base.OnElementChanged(e);
    
                if (e.NewElement != null)
                {
                    Delegate = new WebViewDelegate();
                }
            }
    
            public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
            {
                Console.WriteLine(message.Body);
            }
        }
    
        public class WebViewDelegate : UIWebViewDelegate
        {
            public override void LoadingFinished(UIWebView webView)
            {
                nfloat scale = UIScreen.MainScreen.Scale;
    
                CGSize boundsSize = webView.Bounds.Size;
                nfloat boundsWidth = boundsSize.Width;
                nfloat boundsHeight = boundsSize.Height;
    
                CGSize contentSize = webView.ScrollView.ContentSize;
                nfloat contentHeight = contentSize.Height;
    
                CGPoint offset = webView.ScrollView.ContentOffset;
    
    
                webView.ScrollView.SetContentOffset(CGPoint.Empty, false);
    
                List<UIImage> images = new List<UIImage>();
                while (contentHeight > 0) {
                    UIGraphics.BeginImageContextWithOptions(boundsSize, false, scale);
                    webView.Layer.RenderInContext(UIGraphics.GetCurrentContext());
                    UIImage image = UIGraphics.GetImageFromCurrentImageContext();
                    UIGraphics.EndImageContext();
                    images.Add(image);
    
                    nfloat offsetY = webView.ScrollView.ContentOffset.Y;
                    webView.ScrollView.SetContentOffset(new CGPoint(0, offsetY + boundsHeight), false);
                    contentHeight -= boundsHeight;
                };
    
    
                webView.ScrollView.ContentOffset = offset;
    
                CGSize imageSize = new CGSize(contentSize.Width * scale, contentSize.Height * scale);
                UIGraphics.BeginImageContext(imageSize);
    
                for (int i=0; i<images.Count; i++)
                {
                    var image = images[i];
                    image.Draw(new CGRect(0, scale * boundsHeight * i, scale * boundsWidth, scale * boundsHeight));
                }
    
                UIImage fullImage = UIGraphics.GetImageFromCurrentImageContext();
                UIGraphics.EndImageContext();
    
                var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                var filePath = Path.Combine(folderPath, "test.png");
                fullImage.AsPNG().Save(filePath, false);
            }
        }
    }
    

    Create a delegate for webview to handle the LoadingFinished event. on Android:

    [assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(CustomWebViewRenderer))]
    namespace App.Droid
    {
        public class CustomWebViewRenderer : WebViewRenderer
        {
            public CustomWebViewRenderer(Context context) : base(context)
            {
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
            {
                base.OnElementChanged(e);
    
                if (Control != null)
                {
                    Control.SetWebViewClient(new CustomWebViewClient());
                }
            }
        }
    
        public class CustomWebViewClient : WebViewClient
        {
            public override void OnPageFinished(Android.Webkit.WebView view, string url)
            {
                base.OnPageFinished(view, url);
    
                Bitmap bitmap = Bitmap.CreateBitmap(view.MeasuredWidth, view.ContentHeight, Bitmap.Config.Argb8888);
                Canvas canvas = new Canvas(bitmap);
                Paint paint = new Paint();
                int iHeight = bitmap.Height;
                canvas.DrawBitmap(bitmap, 0, iHeight, paint);
                view.Draw(canvas);
    
                var externalPath = Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath;
                var filePath = System.IO.Path.Combine(externalPath, "test.png");
                var stream = new FileStream(filePath, FileMode.Create);
                bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
                stream.Close();
            }
        }
    }
    

    And we need to enable the EnableSlowWholeDocumentDraw before creating the webview so I placed the code below in the MainActivity:

    ...
    Android.Webkit.WebView.EnableSlowWholeDocumentDraw();
    LoadApplication(new App());
    
    Monday, November 18, 2019 10:00 AM
  • User277514 posted

    var isSaved = fullImage.AsPNG().Save(filePath, false); return true but I am not able to find image in iphone ? Could you tell me where I will find the saved image ? @LandLu

    Tuesday, November 19, 2019 10:17 AM
  • User369979 posted
    Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    

    This is the document folder path of your application's sandbox storage. You can't access it outside your application on a real device. This is why you can't find the image on your iPhone. Try to use a simulator to test this code and the storage of the simulator could be found on your Mac. You can check the image through the filePath.

    Wednesday, November 20, 2019 2:37 AM
  • User2122 posted

    As likely you have, I found this to be a bit too complicated for production work ... so I've abstracted out a solution for use in Xamarin.Forms (iOS, Android, UWP). Usage is quite simple:

    var pngResult = await myHtmlString.ToPngAsync("output.png");

    More than likely, you're going to want to either copy/paste your PNG or Share it. Here is an example of copy/paste:

    ``` async void ShareButton_Clicked(object sender, EventArgs e) { if (Forms9Patch.ToPngfService.IsAvailable) { if (await myHtmlString.ToPngAsync("output.png") is ToFileResult pngResult) { if (pngResult.IsError) using (Toast.Create("PNG Failure", pngResult.Result)) { } else { var collection = new Forms9Patch.MimeItemCollection(); collection.AddBytesFromFile("image/png", pngResult.Result); Forms9Patch.Clipboard.Entry = collection; } } } else using (Toast.Create(null, "PNG Export is not available on this device")) { } }

    ```

    You can find an article with more on how to use it here: https://medium.com/@ben_12456/copy-the-contents-of-a-xamarin-forms-webview-as-a-png-a7461b63d3dd?sk=4d8161dbf6606d032c125340c98bd5e1

    It's MIT licensed open source so enjoy!

    Tuesday, December 31, 2019 7:12 PM