/// <summary> /// Captures page screenshot. /// </summary> /// <param name="webview">The WebView control.</param> /// <param name="settings">The capture settings or null.</param> /// <param name="targetStream">A stream to save the captured image to.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>The task object representing the asynchronous operation.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="webview"/> or <paramref name="targetStream"/> is null. /// </exception> /// <exception cref="DevToolsProtocolException"> /// An error occurred while trying to execute a DevTools Protocol method. /// </exception> /// <exception cref="InvalidOperationException">Other error occurred.</exception> public static async Task CaptureScreenshotAsync(this IChromiumWebView webview, PageCaptureSettings settings, Stream targetStream, CancellationToken cancellationToken) { if (webview is null) throw new ArgumentNullException(nameof(webview)); if (targetStream is null) throw new ArgumentNullException(nameof(targetStream)); CefDictionaryValue args; if (settings is null) { args = null; } else { args = new CefDictionaryValue(); if (settings.Format == ImageCompressionFormat.Jpeg) { args.SetString("format", "jpeg"); if (settings.Quality.HasValue) args.SetInt("quality", settings.Quality.Value); } if (!settings.FromSurface) args.SetBool("fromSurface", false); if (settings.Viewport != null) { PageViewport viewport = settings.Viewport; var viewportDict = new CefDictionaryValue(); viewportDict.SetDouble("x", viewport.X); viewportDict.SetDouble("y", viewport.Y); viewportDict.SetDouble("width", viewport.Width); viewportDict.SetDouble("height", viewport.Height); viewportDict.SetDouble("scale", viewport.Scale); args.SetDictionary("clip", viewportDict); } if (settings.CaptureBeyondViewport) args.SetBool("captureBeyondViewport", true); } byte[] rv = (byte[])await ExecuteDevToolsMethodInternalAsync(webview, "Page.captureScreenshot", args, null, cancellationToken).ConfigureAwait(false); if (rv != null && rv.Length > 11 && rv[rv.Length - 1] == '}' && rv[rv.Length - 2] == '"' && "{\"data\":\"".Equals(Encoding.ASCII.GetString(rv, 0, 9), StringComparison.Ordinal)) { using (var input = new MemoryStream(rv, 9, rv.Length - 11)) using (var base64Transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces)) using (var cryptoStream = new CryptoStream(input, base64Transform, CryptoStreamMode.Read)) { await cryptoStream.CopyToAsync(targetStream, 4096, cancellationToken).ConfigureAwait(false); await targetStream.FlushAsync(cancellationToken).ConfigureAwait(false); } } else { throw new InvalidOperationException(); } }
/// <summary> /// Enables touch on platforms which do not support them. /// </summary> /// <param name="webview">The WebView control.</param> /// <param name="enabled">True whether the touch event emulation should be enabled.</param> /// <param name="maxTouchPoints">Maximum touch points supported. Defaults to one.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>The task object representing the asynchronous operation.</returns> public static async Task SetTouchEmulationEnabledAsync(this IChromiumWebView webview, bool enabled, int? maxTouchPoints, CancellationToken cancellationToken) { if (webview is null) throw new ArgumentNullException(nameof(webview)); var args = new CefDictionaryValue(); args.SetBool("enabled", enabled); if (maxTouchPoints is not null) { if (maxTouchPoints.Value < 1 || maxTouchPoints.Value > 16) throw new ArgumentOutOfRangeException(nameof(maxTouchPoints), "Touch points must be between 1 and 16."); args.SetInt("maxTouchPoints", maxTouchPoints.Value); } ThrowIfNotEmptyResponse(await ExecuteDevToolsMethodInternalAsync(webview, "Emulation.setTouchEmulationEnabled", args, cancellationToken).ConfigureAwait(false)); }