2

Im loading a few images to my canvas and then after they load I want to click a button that saves that canvas image to my server. I can see the script works fine until it gets to the 'toDataURL' part and my function stops executing. What am I doing wrong? Here is my code:

<!DOCTYPE HTML>
<html>
  <head>
  <style>
    body {
    margin: 0px;
    padding: 0px;
   }
  </style>
</head>
<body>
<canvas id="myCanvas" width="578" 

height="200"></canvas>

<div>
<button onClick="saveCards();">Save</button>
</div>

<script>
  function loadImages(sources, callback) 

{
    var images = {};
    var loadedImages = 0;
    var numImages = 0;
    // get num of sources
    for(var src in sources) {
      numImages++;
    }
    for(var src in sources) {
      images[src] = new Image();
      images[src].onload = function() {
        if(++loadedImages >= numImages) 

{
          callback(images);

        }
      };
      images[src].src = sources[src];

    }

  }

  var canvas = 

document.getElementById('myCanvas');
  var context = canvas.getContext('2d');

  var sources = {
    great: 

'images/great.jpg',
    star: 

'images/1Star.jpg',  good: 

'images/good.jpg'
  };

  loadImages(sources, function(images) {
    context.drawImage(images.great, 

0, 0, 80, 120);
    context.drawImage(images.star, 80, 

0, 80, 120);
context.drawImage(images.good, 160, 0, 80, 

120);
  });

</script>

<script type="text/javascript">
function saveCards()
{
var canvas= 
document.getElementById("myCanvas");
alert("stops");
var theString= canvas.toDataURL();

var postData= "CanvasData="+theString;
var ajax= new XMLHttpRequest();
ajax.open("POST", 'saveCards.php', true);
ajax.setRequestHeader('Content-Type', 

'canvas/upload');

ajax.onreadystatechange=function()
{

if(ajax.readyState == 4)
{
alert("image was saved");
}else{
alert("image was not saved");
}
}

ajax.send(postData);
}
</script>

</body>
</html>

Thank you for any help is it because the images are not loaded before toDataUrl is called? If so can you please help me fix it.

This is the php script:

 <?php
if(isset($GLOBALS['HTTP_RAW_POST_DATA']));
{

$rawImage=$GLOBALS['HTTP_RAW_POST_DATA'];
$removeHeaders= 

substr($rawImage,strpos($rawImage, ",")+1);
$decode=base64_decode($removeHeaders);
$fopen= fopen('images/image.png', 'wb');
fwrite($fopen, $decode);
fclose($fopen);
}
?>

I am getting a security error though.

james
  • 109
  • 2
  • 12

2 Answers2

1

In the specification for the canvas element it states:

Information leakage can occur if scripts from one origin can access information (e.g. read pixels) from images from another origin (one that isn't the same).

To mitigate this, bitmaps used with canvas elements are defined to have a flag indicating whether they are origin-clean. All bitmaps start with their origin-clean set to true. The flag is set to false when cross-origin images or fonts are used.

The toDataURL(), toDataURLHD(), toBlob(), getImageData(), and getImageDataHD() methods check the flag and will throw a SecurityError exception rather than leak cross-origin data.

The flag can be reset in certain situations; for example, when a CanvasRenderingContext2D is bound to a new canvas, the bitmap is cleared and its flag reset.

Since you are loading images from a different server into a canvas element, the work-around to be able to use toDataURL() is to "copy" the canvas into a new canvas element to reset the origin-clean flag to "true".

You can see an example of this here

Trendy
  • 460
  • 2
  • 12
  • ok but even when I do this it is still not working... png is default anyway right? Can you think of anything else? – james Nov 22 '13 at 02:17
  • @james What do you mean by "not working"? What error have you got? – Guilherme Sehn Nov 22 '13 at 02:21
  • well i dont think it is getting past that toDataUrl line because when I put an alert anywhere after that it doesn't show up. I don't think the script is making it to the php page. I have a saveCards.php script that saves it to a folder on my server but the image is not showing up. – james Nov 22 '13 at 02:26
  • Are they located on the same server? – Trendy Nov 22 '13 at 02:32
  • I am not sure if there are any errors. How would I check for errors? Thanks – james Nov 22 '13 at 02:33
  • Try console.log(theString) and see if the string is being created properly (easiest in Chrome, which will allow you to click the string). Once you've done that successfully, you should be able to pass it the server-side without a hitch. You need to decode the base64 string without the headers ("data:image/png;base64," should be removed from the string) – Trendy Nov 22 '13 at 02:38
  • huh i tried that and in the console it says uncaught security error: failed to execute 'toDataUrl' on 'htmlcanvasElement' tainted canvases may not be exported.... i don't know why? – james Nov 22 '13 at 02:53
  • Oh, would it be because my website is https... if so how could I get around that? – james Nov 22 '13 at 02:54
  • It's because of the security specifications for the canvas element. There is a workaround for this though, see my updated answer above. – Trendy Nov 22 '13 at 03:11
0

I figured what I did wrong, but not really sure why it works now. In my actual code that I am using instead of images/image.png I was using the full url https://www.mywebsite.com/images/image.png For some reason when I just write the shortened images/image.png . It works fine. Thank you for all the help debugging and for your alternative solutions.

james
  • 109
  • 2
  • 12
  • So all images are from my own website so if anybody knows why using the full url gives the security error, please explain. – james Nov 22 '13 at 04:46