简体   繁体   English

从Google Apps脚本网页上的HTML画布获取数据URI

[英]Get a data URI from a HTML canvas on a Google Apps Script web page

The Problem 问题

I'm trying to implement a signature collector using this library: github.com/szimek/signature_pad 我正在尝试使用以下库来实现签名收集器:github.com/szimek/signature_pad

The collector is running as a web app on Google Apps Script. 收集器在Google Apps脚本上作为网络应用程序运行。 I'd like to be able to use our company's user list and Drive together with the signature to create a PDF. 我希望能够使用我们公司的用户列表和云端硬盘以及签名来创建PDF。

I've created the form using regular HTML and I've added the canvas element from the library's demo page. 我使用常规HTML创建了表单,并从库的演示页面添加了canvas元素。

I can draw a signature but when I try to extract the data URL using canvas.toDataURL() I get the following message: 我可以绘制签名,但是当我尝试使用canvas.toDataURL()提取数据URL时,会收到以下消息:

Expected property "toDataURL" to be a function, not undefined: undefined

in the Chrome developer console. 在Chrome开发者控制台中。

What I've Tried 我尝试过的

I assume that the Google Caja library (of which I know next to nothing) does not implement or allow the toDataURL() method. 我假设Google Caja库(我几乎一无所知)没有实现或不允许toDataURL()方法。 I found this library: http://www.nihilogic.dk/labs/canvas2image/ , that can create a base64 encoded version of a BMP of the canvas. 我找到了这个库: http : //www.nihilogic.dk/labs/canvas2image/ ,可以创建画布的BMP的base64编码版本。

This time I can run the code and extract some data but when I try to put the image back together on the server end, all I get is a black box. 这次我可以运行代码并提取一些数据,但是当我尝试将图像放回服务器端时,得到的只是一个黑匣子。

Here's an example app that demonstrates the problem: https://script.google.com/macros/s/AKfycbwXo0xFNWqiewoe4oh-cSxTdhmqRTyNDwL9xknbtOdk3rLbHZ8/exec 这是一个演示问题的示例应用程序: https : //script.google.com/macros/s/AKfycbwXo0xFNWqiewoe4oh-cSxTdhmqRTyNDwL9xknbtOdk3rLbHZ8/exec

The example outputs 示例输出

Object [object Object] has no method 'toDataURL'

to the developer console. 开发人员控制台。 This should be because I'm using NATIVE sandbox mode instead of EMULATED. 这应该是因为我使用的是NATIVE沙箱模式,而不是EMULATED模式。

Here's a link to the editable project: https:// script.google.com/d/19azWWXnrUO72ryDWhmhKJA-PqoOiDIRNPEIt61h2l_kduUbD87V4P311/edit?usp=sharing. 这是可编辑项目的链接:https://script.google.com/d/19azWWXnrUO72ryDWhmhKJA-PqoOiDIRNPEIt61h2l_kduUbD87V4P311/edit?usp=sharing。 Maybe some nice person can make that a proper link for me? 也许某个好人可以为我建立一个适当的链接?

What Next 接下来是什么

  • Is there a way I can get the toDataURL() method to work? 有没有办法让toDataURL()方法正常工作?
  • Should the BMP solution work? BMP解决方案应该工作吗? What's going wrong? 怎么了
  • Are there other libraries/solutions I could use? 我还可以使用其他库/解决方案吗?

Obviously I haven't provided any code. 显然我没有提供任何代码。 I can do so on request. 我可以要求。 If the BMP solution should work then I'll show you what I'm doing, I haven't added the code now because it may just be a dead end. 如果BMP解决方案能够正常工作,那么我将向您展示我在做什么,我现在还没有添加代码,因为这可能只是一个死胡同。

EDITS: EDITS:

  • Corrected getDataURL() to toDataURL() getDataURL()纠正为toDataURL()
  • Added example app 添加了示例应用

The problem here is that Google Apps Script is running this all in a Caja sandbox (as you know). 这里的问题是Google Apps脚本正在Caja沙箱中全部运行(如您所知)。

The canvas object that the sandbox makes available to us, is a sandbox friendly TameSpecificElement version of canvas, which doesn't have the toDataURL method attached to it. 沙盒提供给我们的canvas对象是沙盒友好的TameSpecificElement版本的画布,它没有附加toDataURL方法。

Whether this is because the Caja team currently deem this as being a potentially dangerous method, or because it is still under development I'm not sure. 这是因为Caja团队当前认为这是一种潜在的危险方法,还是因为它仍在开发中,我不确定。

In the Caja source code : test-domado-canvas-guest has a TODO next to toDataURL, but that doesn't make it clear to me whether thats because implmentation is in development, or just that tests to make sure it is effectively sandboxed are in development, so your guess is as good as mine in terms of future implementation. 在Caja源代码中:test-domado-canvas-guest在toDataURL旁边有一个TODO,但是我不清楚这是否是由于实施正在开发中,或者仅仅是为了确保有效地进行沙盒测试在开发中,因此就将来的实施而言,您的猜测与我的猜测一样好。

But I think its safe to answer that under the current released version, the tamed version of canvas doesn't support that operation. 但是我认为可以肯定地说,在当前发布的版本中,已驯服的canvas版本不支持该操作。

One thing you could use is Canvas' context getImageData method which will give you back an array of all the pixels in it's data property. 可以使用的一件事是Canvas的上下文getImageData方法,该方法将为您返回其data属性中所有像素的数组。 I tested this in your app engine code and it works! 我在您的应用引擎代码中对此进行了测试,并且可以正常工作!

After that you would have to convert that to base64 encoding to roll-your-own dataUrl (might need a good JavaScript library to convert from pixel array to PNG, something like this http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ may do the trick). 在那之后,您必须将其转换为base64编码,再转换为roll-your-own的dataUrl(可能需要一个良好的JavaScript库才能将像素数组转换为PNG,类似http://www.xarg.org/2010/03/ generate-client-side-png-files-using-javascript /可以解决这个问题)。

Here is some sample code that works with the library mentioned, with some efficiency changes vs. your current sample. 这是一些与上述库一起使用的示例代码,与当前示例相比,效率有所变化。

window.saveButton.addEventListener("click", function (event) {
    if (window.signaturePad.isEmpty()) {
        alert("Please provide signature first.");
    } else {
        var captureWidth = 658;
        var captureHeight = 318;

        var p = new PNGlib(captureWidth, captureHeight, 256);
        var background = p.color(0, 0, 0, 0);
        var context = canvas.getContext("2d");
        var imageData = context.getImageData(0, 0, captureWidth, captureHeight);
        var data = imageData.data;
        var dataKeys = Object.keys(data);

        var thisPixelNumber = 0;

        // each pixel is represented by 4 bytes in the array, 
        // so we'll just advance 4 at a time here.  We're using dataKeys to determine the
        // length, because data property in this environment is an object not an array.
        for (var i=0; i < dataKeys.length; i+= 4) {
            var thisPixelNumber = i / 4,
                pixelColor = {
                    r: data[i],
                    g: data[i+1],
                    b: data[i+2],
                    a: data[i+3]
                };

            // only worry about transferring the pixel if it actually has a value, check the     alpha for this.
            // this is a massive time saver for us, reading memory being way faster than writing.
            if (pixelColor.a) {
                var x = thisPixelNumber % captureWidth,
                    y = Math.floor(thisPixelNumber / captureWidth);

                p.buffer[p.index(x, y)] = p.color(pixelColor.r, pixelColor.g, pixelColor.b,     pixelColor.a);
            }
        }

        // in the end just log out the url to the console.  Use Chrome DevTools to check this     value and click it to view the converted PNG
        console.log('data:image/png;base64,' + p.getBase64());
    }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM