2

I'm trying to draw an image inside a canvas element using a data URI. I'm using Django and passing a variable containing the base64 encoding of the photo to the template. In the past, I have used that like so:

<img src="data:image/jpg;base64, {{ photo_data }}">

This works fine. I now want to replace that with this:

<canvas id="canvas"></canvas>

and use javascript to draw the image in the canvas. I tried out the code from this answer:

var canvas = $('#canvas');
var ctx = canvas.getContext('2d');
var img = new Image;
$(img).load(function(){
    ctx.drawImage(img, 0, 0);
});
img.src = "data:image/jpg;base64, {{ photo_data }}"

This gives me the javascript error "SyntaxError: unterminated string literal" on the last line. I read that this error usually happens because of line breaks. Upon inspecting the page source it looks like there are line breaks in there:

enter image description here

So then I tried removing line breaks:

function removeBreaks(data){
    return data.replace(/(\r\n|\n|\r)/gm, "");
}

var photo_data = removeBreaks({{ photo_data }});

Now I'm getting a new error when I call removeBreaks. "SyntaxError: identifier starts immediately after numeric literal".

var photo_data = removeBreaks(/9j/4AAQSkZ...
----------------------------------^

Is there some sort of escaping I need to do, and if so how would I do it?

Update

I tried all of the suggestions from the comments and the answer. So far the thing that has come closest to working is doing

photo_data = json.dumps(<base64 encoded image file>)

on the Django side and

var photo_data = "{{ photo_data }}".replace(/&quot;/g,"");
img.src = "data:image/jpg;base64, " + photo_data;

on the Javascript side. The image doesn't show at all if I don't replace the &quot;s, and when I do remove them only the top third of the image shows. I tried resizing the canvas element, that doesn't seem to be the issue.

Update #2

Turns out the canvas size was the issue. Width and height of the canvas element need to be as big or bigger than the width and height of the image, which I wasn't setting when I created the Image object. Both the accepted answer and json.dumps (with the &quot; caveat) work to solve the original problem.

Community
  • 1
  • 1
snorthway
  • 576
  • 1
  • 10
  • 15
  • Try `removeBreaks("{{ photo_data }}");` – Matt Jul 25 '13 at 19:45
  • I did, that gives the "unterminated string literal" error again. – snorthway Jul 25 '13 at 19:46
  • You're going to have to transform that block of text so that the real newlines are replaced with `\n` - a backslash and an "n", which is how JavaScript expects line breaks to be encoded in strings. You have to do it on the Django side, not the JavaScript side. – Pointy Jul 25 '13 at 19:49
  • Try using a JSON encoder function to encode the image data. Then prepend the data URL header to that. Something like `img.src = "data:image/jpg;base64, " + {{ json.dumps(photo_data) }};` maybe (I'm not a Python programmer). – Pointy Jul 25 '13 at 19:51
  • 1
    Your multi-line data string is causing a *syntax error*. You are expecting JavaScript to clean up a syntactically broken string by passing it to a JavaScript function. That JavaScript function can't run because there's a syntax error in its invocation (caused by the multi-line string). Instead, you *must* fix this problem in the server-side code the generates the JavaScript string. Django is the system that put the line breaks there in the first place; surely Django has some function to eliminate them? – apsillers Jul 25 '13 at 19:54
  • One other thing: you need quotes around the value, like `removeBreaks("{{ photo_data }}")`. When you do that, you'll see that you're back to the same old error. I'd advise adding a `[django]` tag and adding more information about what you're doing on the server. – apsillers Jul 25 '13 at 20:02
  • Found a possible duplicate: [Django template filter - one line](http://stackoverflow.com/questions/3717683/django-template-filter-one-line) (this will eliminate syntax error, but it might also ruin your base64 value) – apsillers Jul 25 '13 at 20:10
  • apsillers You're right, it was silly of me to try to fix a syntactically broken string by passing it to a function. Also the question has been updated with django tag. @Pointy json dumps sort of worked, see update. – snorthway Jul 25 '13 at 21:42
  • @snorthway wow where do the `"` characters come from? If the JSON encoder in Python/Django does that, it's **seriously** broken, so I kind-of doubt it. Have you checked to see what the generated JavaScript source looks like in various scenarios? – Pointy Jul 25 '13 at 22:40

1 Answers1

1

I do it like this:

img.src = "1234 ... 56789" + //
   "01234567 ... 7890" + //
   .....
   "67879878907890";

Some people I work with like to put the + on the front of the line, claiming its easier to read.

Assume you have the image data, base64 encoded in a string. A sort of pseudo C/Java/Javascript like language would have code something like this (since I don't know Python):

String image64 = ...
String output = "img.src = \"data:image/jpg;base64, ";

for (int i = 0; i < image64.length ; i += 100) {
    if (i > 0) {
        output += "\" + // \n    \"";
    }
    if (i + 100 < image64.length) {
        output += image64.substring(i, i+100);
    } else {
        output += image64.substring(i); // this gets the rest
    }
}
output += "\";\n";

Then you write the value of output to where ever you are putting your javascript.

Now a quick look a Django and Python stuff leads me to suspect it would look something like this in the template:

img.src = "data:image/jpg;base64, " + //
{% for line in photo_data_lines %}
    "{{ line }}" + //
{% endfor %}
    "";

But you'd have to set up the photo_data_lines array with 100 character chunks of the image data in each element beforehand. (Or however long you want the chunks.)

Lee Meador
  • 12,829
  • 2
  • 36
  • 42
  • In this case the image data isn't there as a literal, it's in a Python string (or something like that). The JavaScript is being generated. – Pointy Jul 25 '13 at 19:54
  • Have the Python write 100 chars from the image and then `" + // \n\t"` to make it write the javascript correctly. You are using Python to write Javascript and it has to be valid. – Lee Meador Jul 25 '13 at 19:57
  • Yes that'd work, but I bet the Python JSON encoder will turn the embedded newlines into `\n` escapes. – Pointy Jul 25 '13 at 20:00
  • Because it's an easy way to get something to sanitize a string for inclusion in JavaScript source code. Anything that's syntactically valid JSON is also valid JavaScript. – Pointy Jul 25 '13 at 20:17
  • So ... would you expect there to be funky characters inside your base64 rendering of an image? The character content is extremely limited. – Lee Meador Jul 25 '13 at 20:22
  • Yes, except for newlines :-) A JSON encoder will translate the newlines into `\n` (a backslash character and an "n"). – Pointy Jul 25 '13 at 20:43
  • So the idea I added of breaking it up into a Python array where each element has a chunk of characters from the original base64 image string should work fine. – Lee Meador Jul 25 '13 at 21:33
  • Lee, I tried both methods. The first one worked about as well as using a json encoder (which still hasn't entirely worked), but I felt like the json encoder was cleaner. The second one didn't work at all, I'm not sure why. – snorthway Jul 25 '13 at 21:49
  • When it works, you don't have much incentive for trying more ways. Good enough beats unknown every time. – Lee Meador Jul 25 '13 at 21:56
  • I'm accepting this answer because it does work, and the problem I put in the update didn't have anything to do with the data URI. Thanks everyone for the help. – snorthway Jul 26 '13 at 17:19