しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

WebView のページイメージを丸ごと画像として保存する

ストアアプリ内で WebView を利用する場合に、スクロールキャプチャソフトのようにページ全体を画像としてキャプチャする方法はないかと探していたら普通に WebView のメソッドありました。

WebView.CapturePreviewToStreamAsync method (Windows)

ただこの API リファレンスにのっているコードをそのまま書いてもなぜか Exception 発生;

いろいろ試行錯誤した結果、下記のように処理すればできました

デフォルトだと BMP 形式の未圧縮で保存してしまうので、PNG 形式にエンコードして保存しています

/// <summary>
/// WebView の ページ遷移完了イベントハンドラ
/// </summary>
/// <param name="sender">イベント発行者</param>
/// <param name="args">イベント引数</param>
private async void OnNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
    if (!args.IsSuccess)
    {
        return;
    }

    var widthString = await this.webView.InvokeScriptAsync("eval", new[] { "document.documentElement.scrollWidth.toString()" });
    var heightString = await this.webView.InvokeScriptAsync("eval", new[] { "document.documentElement.scrollHeight.toString()" });

    double width = 0;
    double height = 0;

    if (!double.TryParse(widthString, out width) || !double.TryParse(heightString, out height))
    {
        return;
    }
    //メモリ節約のため上限設定
    if (height > 8192)
    {
        height = 8192;
    }
    this.webView.Height = height;
    this.webView.Width = width;

    // 古いファイルの掃除
    foreach (var removeFile in await ApplicationData.Current.TemporaryFolder.GetFilesAsync())
    {
        try
        {
            if (".bmp".Equals(Path.GetExtension(removeFile.Name).ToLower()))
            {
                await removeFile.DeleteAsync();
            }
        }
        catch (Exception)
        {
        }
    }

    // WebView の画像イメージ保存
    var tmpFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("tmp.bmp", CreationCollisionOption.GenerateUniqueName);

    // WebView の内容をキャプチャして png 書き出し
    using (var ms = await tmpFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        await this.webView.CapturePreviewToStreamAsync(ms);
        await ms.FlushAsync();

        ms.Seek(0);

        var decoder = await BitmapDecoder.CreateAsync(ms);
        var data = await decoder.GetPixelDataAsync();
        var file = await KnownFolders.PicturesLibrary.CreateFileAsync(string.Format("{0}.png", Guid.NewGuid().ToString()), CreationCollisionOption.GenerateUniqueName);

        using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);

            encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Straight,
                (uint)decoder.PixelWidth,
                (uint)decoder.PixelHeight,
                decoder.DpiX,
                decoder.DpiY,
                data.DetachPixelData());

            encoder.BitmapTransform.Bounds = new BitmapBounds() { Width = decoder.PixelWidth, Height = (uint)height };

            // ファイルに書き出し
            await encoder.FlushAsync();
        }
    }
}

それでも長すぎるページをキャプチャした場合は途中からレンダリングされなかったり、 Exception 出ちゃうみたいですが・・・

きっと完璧にするには分割してキャプチャするしかないんでしょうね