2

I have a directive in angular JS that allows exporting SVGs to a PNG. This works fine in other browsers, but in IE I get a security error. I have tried all sorts of things, but it just doesn't seem to work. I read somewhere that if I base64 encode it, then that would work, but it doesn't.

The code I have for drawing the SVG on the canvas is this:

// Private function for drawing our images
var drawImage = function (canvas, ctx, svgContainer) {

    // Defer our promise
    var deferred = $q.defer();

    // Remove hidden layers
    removeHidden(angular.element(clone));

    // Create our data
    var clone = angular.element(svgContainer[0].cloneNode(true)),
        child = clone.children()[0];

    // Remove hidden layers
    removeHidden(angular.element(clone));

    var s = new XMLSerializer(),
        t = s.serializeToString(child),
        base64 = 'data:image/svg+xml;base64,' + window.btoa(t);

    console.log(base64);

    var img = new Image();

    // When the image has loaded
    img.onload = function () {

        // Create our dimensions
        var viewBox = child.getAttribute('viewBox').split(' ');

        console.log(viewBox);

        var dimensions = {
            width: viewBox[2],
            height: viewBox[3]
        };

        console.log(img.width);

        // Get our location
        getNextLocation(canvas.width, canvas.height, dimensions);

        // Draw our image using the context
        ctx.drawImage(img, 0, 0, dimensions.width / 2, dimensions.height, location.x, location.y, location.width, location.height);

        // Resolve our promise
        deferred.resolve(location);
    };

    // Set the URL of the image
    img.src = base64;

    // Return our promise
    return deferred.promise;
};

Before this I was creating a blob but that also caused the security error. My main bit of code, is this bit here:

// Public function to generate the image
self.generateImage = function (onSuccess) {

    // Reset our location
    location = null;
    counter = 0;

    // Get our SVG
    var target = document.getElementById(self.options.targets.containerId),
        svgContainers = angular.element(target.getElementsByClassName(self.options.targets.svgContainerClassName)),
        itemCount = svgContainers.length;

    // Get our context
    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d');

    // Set our canvas height and width
    setCanvasDimensions(canvas, itemCount);

    // Create our array of promises
    var promises = [];

    // Draw our header and footer
    drawHeader(canvas, ctx);

    //// For each container
    //for (var i = 0; i < itemCount; i++) {

    //    // Get our elements
    //    var svgContainer = svgContainers[i];

    //    // Draw our image and push our promise to the array
    //    promises.push(draw(canvas, ctx, svgContainer));
    //}

    promises.push(draw(canvas, ctx, svgContainers[0]));

    // Finally add our footer to our promises array
    promises.push(drawFooter(canvas, ctx));

    // When all promises have resolve
    $q.all(promises).then(function () {

        console.log('all promises completed');

        // Get our URL as a base64 string
        var dataURL = canvas.toDataURL("image/png");

        console.log('we have the image');

        // Create our model
        var model = {
            teamName: self.options.team.name,
            sport: self.options.team.sport,
            data: dataURL
        };

        // Create our preview
        self.create(model).then(function (response) {

            console.log('saved to the database');

            // Invoke our success callback
            onSuccess(response);
        });
    })
};

As you can see I loop through each svg container and draw the SVG for each one. I have commented that out in this bit of code and just draw the first image. The console.log directive after canvas.toDateURL never get's invoked and I get a security error.

The SVG is inline. From what I have read the issue might be due to the xlns declaration, but removing it still gives me the issue:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
   viewBox="0 0 259.5 131" enable-background="new 0 0 259.5 131" xml:space="preserve">

Does anyone know how I can solve this issue?

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • Please post the image sources that are being loaded; not the actual files, but the data that you pass to the function. – Stephan Bijzitter Oct 27 '15 at 09:41
  • I can't do that, the inline SVGs have their colours changed dynamically so it has to be them that I draw onto the canvas – r3plica Oct 27 '15 at 09:42
  • dumb, really dumb suggestion. does jpg work in lieu of png? I got zinged on this lately, where .jpg file worked but .png was a no go... – zipzit Oct 27 '15 at 09:49
  • It doesn't matter to me what format it gets converted to, I have an image in the footer which get's drawn fine (the source of that is a png) and that doesn't taint the canvas, it is only the SVG that taint's it :( – r3plica Oct 27 '15 at 09:58
  • So why can't you do that? You say you have inline SVGs, show them. – Stephan Bijzitter Oct 27 '15 at 10:21
  • what do you mean by show them? – r3plica Oct 27 '15 at 10:27
  • 3
    SVG drawn on IE's canvas element will taint that canvas and will automatically disable `.toDataURL`. IE has this security restriction that other browsers don't. The workaround is to either use .jpg/.png or to convert the svg commands into canvas commands (eg, Mike Swansons nice Adobe Illustrator plugin: https://github.com/mikeswanson/Ai2Canvas). – markE Oct 27 '15 at 18:02

1 Answers1

0

You need to set img.crossOrigin= 'anonymous'; before img.src=...

However, to make this work, you will also need to have access-control-allow-origin: * set in the response header of your image.

to try the solution, you can use 'http://fabricjs.com/assets/printio.png'(which has the proper header set) for example

jilykate
  • 5,430
  • 2
  • 17
  • 26
  • No the question at hand is about an IE limitation where drawing SVG would taint the canvas; It has nothing to do with same-origin policies and img.crossOrigin won't fix anything. – Kaiido Jan 28 '19 at 13:02