WebBrowser HTML文档到图像

问题描述 投票:1回答:2

我正在尝试制作网页图像,但有些页面将我显示为白页。我还尝试将应用程序的可执行文件添加到注册表中的FEATURE_BROWSER_EMULATION部分,但是无法正常工作。

[编辑]我更改了脚本,但是当我使用window.onload()时,仅由0调用的Java脚本和vb脚本组成的页面仍显示白色图像,其高度设置为browser.Document.Body.ScrollRectangle.Height

我在IE中启用了JavaScript,但没有发现不同。

private void makepicture(Uri url)
{
    using (WebBrowser browser = new WebBrowser())
    {
        browser.ScrollBarsEnabled = false;
        browser.ScriptErrorsSuppressed = true;
        browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(DocumentCompleted);
        browser.Navigate(url, null, null, "User-Agent: User agent");
        while (browser.ReadyState != WebBrowserReadyState.Complete)
        {
            Application.DoEvents();
        }
    }
}

private void DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    WebBrowser browser = sender as WebBrowser;
    if (browser.ReadyState != WebBrowserReadyState.Complete) return;
    browser.ClientSize = new Size(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Height);
    using (Bitmap bitmap = new Bitmap(browser.ClientSize.Width, browser.ClientSize.Height))
    {
        browser.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, browser.ClientSize.Width, browser.ClientSize.Height));
        using (MemoryStream stream = new MemoryStream())
        {
            bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            byte[] bytes = stream.ToArray();
            picbytes = bytes;
        }
    }
}

public static Bitmap ByteToImage(byte[] blob)
{
    MemoryStream mStream = new MemoryStream();
    byte[] pData = blob;
    mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
    Bitmap bm = new Bitmap(mStream, false);
    mStream.Dispose();
    return bm;
}
c# winforms bitmap webbrowser-control
2个回答
0
投票

尝试这样设置用户代理

browser.Navigate(url, null, null, "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0");

0
投票

为了打印WebBrowser控件的HTML内容,需要考虑以下几点:

  1. 我们需要使用WebBrowser的DocumentCompleted事件来确定当前文档何时加载和呈现
  2. 一个文档可能(将)包含一个以上的子文档,通常包含在Frames / IFrames中。每个IFrame都有其自己的Document:加载IFrame中包含的Document时,会重新生成DocumentCompleted。这意味着当WebBrowser导航到URL时,该事件可以并且将引发多次。

    这里的注释进一步说明:How to get an HtmlElement value inside Frames/IFrames?

  3. WebBrowser的托管属性并不总是反映DOM的实际值。例如,完成渲染后,Html文档的实际尺寸不会在任何地方反映出来,因此我们需要从DOM自己获取这些度量。当前DOM呈现的尺寸由以下引用:

    [WebBrowser].Document.DomDocument.documentElement.scrollHeight;
    [WebBrowser].Document.DomDocument.documentElement.scrollWidth;
    

    参见:Measuring Element Dimension and Location with CSSOM in Windows Internet Explorer

  4. WebBrowser控件DrawToBitmap()方法是从Control派生的,但实际上并未像我们期望的那样实现。其他控件也是如此:使用此方法时,RichTextBox可以打印空白内容。

  5. 一个HTML文档可能大于位图支持的最大大小。还有一个更细微的内存限制:Bitmap对象需要将其内容存储在连续的内存空间中,因此,实际上很难预先确定Bitmap大小的限制,并且在我们可能不期望的时候可能会导致异常。
  6. WebBrowser控件的“仿真功能”必须设置为Internet Explorer11。请参阅:How can I get the WebBrowser control to show modern contents?Web browser control emulation issue (FEATURE_BROWSER_EMULATION)

首先,订阅WebBrowser控件的DocumentCompleted事件。

A Dictionary<Uri, Bitmap>在这里用于存储表示在会话中访问的URL的HTML内容的位图。引发DocumentCompleted事件时,如果以前从未访问过当前URL,则会向Dictionary中添加一个新元素。如果已经存储了URL,则我们将更新相关的Bitmap对象,因此在集合中仅存在Html文档的最新快照。

我正在使用支持类来处理位图的创建,并声明用于从当前ISurfacePresenter生成位图的本机COM接口。由于WebBrowser控件是forced,以便将VIEW_OBJECT_COMPOSITION_MODE_LEGACY用作所有站点的CompositionMode,因此内部GetPrintBitmap在这种情况下将调用IViewObject Interface Draw()方法,所以我们也这样做。

要打印当前HTML文档的内容(所有内容),请调用DrawContent(WebBrowser browser)类的WebBrowserExtender静态方法:

Dictionary<Uri, Bitmap> browserShots = new Dictionary<Uri, Bitmap>();

private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    var browser = sender as WebBrowser;
    if (browser.ReadyState != WebBrowserReadyState.Complete) return;

    var bitmap = WebBrowserExtender.DrawContent(browser);
    if (bitmap != null) {
        if (!browserShots.ContainsKey(browser.Url)) {
            browserShots.Add(browser.Url, bitmap);
        }
        else {
            browserShots[browser.Url]?.Dispose();
            browserShots[browser.Url] = bitmap;
        }
        // Show the Bitmap in a  PictureBox control, eventually
        [PictureBox].Image = browserShots[browser.Url];
    }
}

WebBrowserExtender支持类:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class WebBrowserExtender
{
    public static Bitmap DrawContent(WebBrowser browser)
    {
        if (browser.Document == null) return null;
        Size docSize = Size.Empty;
        Graphics g = null;
        var hDc = IntPtr.Zero;

        try {
            docSize.Height = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollHeight;
            docSize.Width = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollWidth;
            docSize.Height = Math.Max(Math.Min(docSize.Height, 32750), 1);
            docSize.Width = Math.Max(Math.Min(docSize.Width, 32750), 1);

            var previousSize = browser.ClientSize;
            browser.ClientSize = new Size(docSize.Width, docSize.Height);

            var bitmap = new Bitmap(docSize.Width, docSize.Height, PixelFormat.Format32bppArgb);
            g = Graphics.FromImage(bitmap);
            var rect = new RECT(0, 0, bitmap.Width, bitmap.Height);
            hDc = g.GetHdc();
            var view = browser.ActiveXInstance as IViewObject;
            view.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hDc, ref rect, IntPtr.Zero, IntPtr.Zero, 0);
            browser.ClientSize = previousSize;
            return bitmap;
        }
        catch {
            // This catch block is like this on purpose: nothing to do here
            return null;
        }
        finally {
            if (hDc != null) g?.ReleaseHdc(hDc);
            g?.Dispose();
        }
    }

    [ComImport]
    [Guid("0000010D-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IViewObject
    {
        void Draw(uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, 
                  IntPtr hdcTargetDev, IntPtr hdcDraw, ref RECT lprcBounds, 
                  [In] IntPtr lprcWBounds, IntPtr pfnContinue, uint dwContinue);
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        public RECT(int left, int top, int width, int height)
        {
            Left = left; Top = top; Right = width; Bottom = height;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.