3

问题

我正在尝试使用此库实现签名收集器:github.com/szimek/signature_pad

收集器在 Google Apps Script 上作为 Web 应用程序运行。我希望能够使用我们公司的用户列表和 Drive 以及签名来创建 PDF。

我使用常规 HTML 创建了表单,并从库的演示页面添加了 canvas 元素。

我可以绘制签名,但是当我尝试使用提取数据 URL 时canvas.toDataURL()收到以下消息:

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

在 Chrome 开发者控制台中。

我试过的

我假设 Google Caja 库(我几乎一无所知)没有实现或允许该toDataURL()方法。我找到了这个库:http ://www.nihilogic.dk/labs/canvas2image/ ,它可以创建画布 BMP 的 base64 编码版本。

这次我可以运行代码并提取一些数据,但是当我尝试在服务器端将图像重新组合在一起时,我得到的只是一个黑匣子。

这是一个演示问题的示例应用程序:https ://script.google.com/macros/s/AKfycbwXo0xFNWqiewoe4oh-cSxTdhmqRTyNDwL9xknbtOdk3rLbHZ8/exec

示例输出

Object [object Object] has no method 'toDataURL'

到开发者控制台。这应该是因为我使用的是 NATIVE 沙盒模式而不是 EMULATED。

这是可编辑项目的链接:https://script.google.com/d/19azWWXnrUO72ryDWhmhKJA-PqoOiDIRNPEIt61h2l_kduUbD87V4P311/edit?usp=sharing。也许某个好人可以为我建立一个适当的链接?

接下来是什么

  • 有没有办法让我的toDataURL()方法起作用?
  • BMP 解决方案应该起作用吗?怎么了?
  • 我可以使用其他库/解决方案吗?

显然我没有提供任何代码。我可以根据要求这样做。如果 BMP 解决方案应该工作,那么我会告诉你我在做什么,我现在没有添加代码,因为它可能只是一个死胡同。

编辑:

  • 更正getDataURL()toDataURL()
  • 添加了示例应用程序
4

1 回答 1

1

这里的问题是 Google Apps 脚本在 Caja 沙箱中运行这一切(如您所知)。

沙盒提供给我们的canvas对象是一个沙盒友好TameSpecificElement版本的画布,它没有toDataURL附加方法。

这是因为 Caja 团队目前认为这是一种潜在的危险方法,还是因为它仍在开发中,我不确定。

在 Caja 源代码中:test-domado-canvas-guest 在 toDataURL 旁边有一个 TODO,但这并不能让我清楚这是因为实施正在开发中,还是只是为了确保它被有效沙盒化的测试是在开发中,所以你的猜测在未来的实施方面和我的一样好。

但我认为可以安全地回答,在当前发布的版本下,canvas 的驯服版本不支持该操作。

您可以使用的一件事是 Canvas 的 contextgetImageData方法,它将为您返回一个包含其data属性中所有像素的数组。我在您的应用引擎代码中对此进行了测试,并且可以正常工作!

之后,您必须将其转换为 base64 编码以滚动您自己的 dataUrl(可能需要一个好的 JavaScript 库来将像素数组转换为 PNG,类似于http://www.xarg.org/2010/03/ generate-client-side-png-files-using-javascript/可以解决问题)。

这是一些与提到的库一起使用的示例代码,与您当前的示例相比,有一些效率变化。

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());
    }
});
于 2013-11-08T14:15:23.603 回答