在后端ASP.NET上渲染Chart.js

时间:2014-11-18 15:29:33

标签: asp.net .net chart.js

我使用Chart.js在我的前端ASP.NET应用程序上显示图表。现在我需要在后端生成PNG文件。是否可以在没有Web浏览器的情况下渲染画布?我应该使用哪种技术?

2 个答案:

答案 0 :(得分:1)

以下是我在类似情况下使用的一些代码,它采用了一些html并生成了它的图像(裁剪空白)。我不确定在页面生命周期中Chart.js何时绘制它的图表但是这可能不起作用。您可以调整它以加载实际页面而不是传入HTML。

不漂亮,不是平台无关,并且可能会有不同的结果,具体取决于IE等的版本(特别是考虑到Chart.js需要IE上的polyfill< = 8)。但它适用于我需要它的情况。

public class HtmlToImageConverter
{
    public string Html { get; set; }
    public Bitmap Image { get; set; }

    public HtmlToImageConverter(string html)
    {
        this.Html = html;
    }

    public Bitmap Render()
    {
        var thread = new Thread(GenerateInternal);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
        return Image;
    }

    private void GenerateInternal()
    {
        var webBrowser = new WebBrowser
        {
            ScrollBarsEnabled = false, 
            DocumentText = this.Html,
            ClientSize = new Size(3000, 3000)
        };

        webBrowser.DocumentCompleted += WebBrowser_DocumentCompleted;
        while (webBrowser.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
        webBrowser.Dispose();
    }

    private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        var webBrowser = (WebBrowser)sender;

        this.Image = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
        webBrowser.BringToFront();
        webBrowser.DrawToBitmap(Image, webBrowser.Bounds);

        this.Image = AutoCrop(this.Image);
    }

    private static byte[][] GetRgb(Bitmap bmp)
    {
        var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        var ptr = bmpData.Scan0;
        var numPixels = bmp.Width * bmp.Height;
        var numBytes = bmpData.Stride*bmp.Height;
        var padding = bmpData.Stride - bmp.Width*3;
        var i = 0;
        var ct = 1;

        var r = new byte[numPixels];
        var g = new byte[numPixels];
        var b = new byte[numPixels];
        var rgb = new byte[numBytes];

        Marshal.Copy(ptr, rgb, 0, numBytes);

        for (var x = 0; x < numBytes - 3; x += 3)
        {
            if (x == (bmpData.Stride*ct - padding))
            {
                x += padding;
                ct++;
            }

            r[i] = rgb[x];
            g[i] = rgb[x + 1];
            b[i] = rgb[x + 2]; i++;
        }

        bmp.UnlockBits(bmpData);
        return new[] { r, g, b };
    }

    private static Bitmap AutoCrop(Bitmap bmp)
    {
        //Get an array containing the R,G,B components of each pixel
        var pixels = GetRgb(bmp);

        var h = bmp.Height - 1;
        var w = bmp.Width;
        var top = 0;
        var bottom = h;
        var left = bmp.Width;
        var right = 0;
        var white = 0;

        const int tolerance = 95;

        var prevColor = false;
        for (var i = 0; i < pixels[0].Length; i++)
        {
            int x = (i % (w)), y = (int)(Math.Floor((decimal)(i / w)));
            const int tol = 255 * tolerance / 100;
            if (pixels[0][i] >= tol && pixels[1][i] >= tol && pixels[2][i] >= tol)
            {
                white++;
                right = (x > right && white == 1) ? x : right;
            }
            else
            {
                left = (x < left && white >= 1) ? x : left;
                right = (x == w - 1 && white == 0) ? w - 1 : right;
                white = 0;
            }

            if (white == w)
            {
                top = (y - top < 3) ? y : top;
                bottom = (prevColor && x == w - 1 && y > top + 1) ? y : bottom;
            }

            left = (x == 0 && white == 0) ? 0 : left;
            bottom = (y == h && x == w - 1 && white != w && prevColor) ? h + 1 : bottom;

            if (x == w - 1)
            {
                prevColor = (white < w);
                white = 0;
            }
        }

        right = (right == 0) ? w : right;
        left = (left == w) ? 0 : left;

        //Crop the image
        if (bottom - top > 0)
        {
            return bmp.Clone(new Rectangle(left, top, right - left + 1, bottom - top), bmp.PixelFormat);
        }

        return bmp;
    }
}

答案 1 :(得分:0)

您可能想看看Puppeteer(https://pptr.dev/),它使您可以从服务器以编程方式访问浏览器功能。我还可以强烈推荐无浏览器(https://www.browserless.io/),它基于Puppeteer提供了更多功能。

两者都是开源的,Browserless.io也提供了托管选项。 /screenshot API端点可能是您要寻找的:https://docs.browserless.io/docs/screenshot.html。它以URL作为输入并返回页面图像。

如果您拥有HTML但没有指向网站的URL,则还可以在URL参数中传递编码为base64的HTML内容,例如:

data:text/html;charset=UTF-8;base64,dGVzdAo=