19

I have a HTML div with external images. (The following is an example, but in the actual case I am using Amazon S3, so downloading and storing the image on the same server is not an option) Currently I am using html2canvas to convert the div to image. However, the external image is always replaced by a blank space.

The code I use to capture the image:

$(function() { 
    $("#btnSave").click(function() { 
        html2canvas($("#widget"), {
            onrendered: function(canvas) {
                theCanvas = canvas;
                document.body.appendChild(canvas);

                // Convert and download as image 
                Canvas2Image.saveAsPNG(canvas); 
                $("#img-out").append(canvas);
            }
        });
    });
}); 

Edited: jsfiddle: https://jsfiddle.net/0y2zxxL8/3/

I may use other library. I may also do that in backend. (I am using PHP + laravel 5 for backend) Is there a way I can generate a "screenshot" of the HTML div with external images?

Update The current answer are working after editing. Yet, for my actual use, there will be multiple image with their position set by the user by drag and drop. I can still get the position, but it would be better for me if it is possible to not set the position specifically.

cytsunny
  • 4,838
  • 15
  • 62
  • 129
  • 1
    I am no expert in the field. But if you're looking for as-good-as-it-gets results and you can do it server side you could take a look at [phantomJs](http://phantomjs.org/) and [nightmareJs](http://www.nightmarejs.org/). – DevK Jan 03 '17 at 15:40
  • @devk Yes, I know that and that's why I said I don't mind doing that in backend. – cytsunny Jan 04 '17 at 02:32
  • 1
    just useCORS:true, [ref](https://stackoverflow.com/a/33220748) – Pavan Nov 02 '18 at 07:46

3 Answers3

11

Your JSFiddle given ReferenceError: Canvas2Image is not defined while hit on 'Save PNG' button. So check your code(not fiddle) for same error.

UPDATE
See this JSFiddle example as reference. May this one will help you.

UPDATE 2
I place some code into your one, check it out. Hope this will be your solution..!

Working result with FF v44 and its working. Taken snap with JSFiddle code enter image description here

$(function() { 
    $("#btnSave").click(function() { 
        html2canvas($("#widget"), {
            onrendered: function(canvas) {
                var context=canvas.getContext("2d"); // returns the 2d context object
                var img=new Image() //creates a variable for a new image

                img.src= "http://lorempixel.com/400/200/"; // specifies the location of the image
                context.drawImage(img,0,50); // draws the image at the specified x and y location
                // Convert and download as image 
                theCanvas = canvas;
                document.body.appendChild(canvas);

                // Convert and download as image 
                Canvas2Image.saveAsPNG(canvas); 
                $("#img-out").append(canvas);
            }
        });
    });
});

UPDATE 3 (12/29/2016) JSFiddle

After research, found that the problem is because we are only assigning the image source which is only pass message to the browser to retrieve the data.

Further image element is may be not really accessible with the browser when click to draw canvas from it. We are just tell code to load it and it's done.

Change code to solve OP's facing chrome issue like below:

img.onload = function() {
  context.drawImage(img, 0, 50);// draws the image at the specified x and y location
}

UPDATE 4 JSFiddle
Make image position dynamic based on OP requirement.

AddWeb Solution Pvt Ltd
  • 21,025
  • 5
  • 26
  • 57
  • I have tried removing the line Canvas2Image.saveAsPNG(canvas);. That removes the error but still the problem is not solved. https://jsfiddle.net/0y2zxxL8/3/ – cytsunny Dec 21 '16 at 06:10
  • Seems it is still not working with the new link you provided. http://jsfiddle.net/j17mwmjm/36/ – cytsunny Dec 21 '16 at 07:07
  • Basically what problem you see on jsfiddle is what I see in my project. It fails with no error message. There is only a blank space instead of the external image in the output image. – cytsunny Dec 21 '16 at 07:35
  • @cytsunny Check my **UPDATE 2** – AddWeb Solution Pvt Ltd Dec 21 '16 at 08:15
  • No, it is not working. This is the jsfiddel testing your solution. https://jsfiddle.net/0y2zxxL8/6/ – cytsunny Dec 22 '16 at 02:02
  • @cytsunny: I placed my working code info. Check my snap from *UPDATE 2* – AddWeb Solution Pvt Ltd Dec 22 '16 at 05:06
  • I have retried with Firefox. It works in Firefox, but not in Chrome. – cytsunny Dec 22 '16 at 06:02
  • In which chrome version? – AddWeb Solution Pvt Ltd Dec 29 '16 at 05:47
  • Chrome ver 55.0.2883.87 m on Windows 10. – cytsunny Dec 29 '16 at 05:50
  • If you don't mind, I have an another approach to accomplish this. You want to see that? – AddWeb Solution Pvt Ltd Dec 29 '16 at 05:55
  • Of course. In the question, I have already stated that I don't mind using other library or do it in backend. The only limit is that I need the image to be stored remotely, without download and storing temporarily. – cytsunny Dec 29 '16 at 06:06
  • Yes, Ride [Simple Implementation of html2canvas With JavaScript and PHP](http://kubilayerdogan.net/screenshot/index.php) and check for cross browser compatibility. Once it working you can see it's code [here](http://www.kubilayerdogan.net/html2canvas-take-screenshot-of-web-page-and-save-it-to-server-javascript-and-php/) – AddWeb Solution Pvt Ltd Dec 29 '16 at 06:13
  • Note: This code is made with PHP and JS but you can push it into laravel. – AddWeb Solution Pvt Ltd Dec 29 '16 at 06:14
  • The image you use is host with the same domain. The main point for this question is external image. Any example for image that is not in the same domain? (You may use the image I used in the jsFiddle) – cytsunny Dec 29 '16 at 06:20
  • I tried, but it is still not working on chrome. Changed image link: http://i.imgur.com/ywkiqqR.png And the result: http://i.imgur.com/VKtkT20.png – cytsunny Dec 29 '16 at 06:30
  • @cytsunny Check my *UPDATE 3* this should solve your problem with Chrome... – AddWeb Solution Pvt Ltd Dec 29 '16 at 10:14
  • I accept this is a working solution, but this will require me to specifically set the image position. For my actual use, there will be multiple image with their position set by the user by drag and drop. I can still get the position, but it would be better for me if it is possible to not set the position specifically. I would like to wait for better answer, and will award you the bounty if there are no better answer. – cytsunny Dec 29 '16 at 10:49
  • @cytsunny: If you happy with my answer and efforts, please accept/provide bounty. – AddWeb Solution Pvt Ltd Dec 30 '16 at 05:44
  • The new update doesn't improve much. What you have done is what I would expect I need to do in my actual case, but that is exactly I want to avoid. (And in my actual use, I will need to handle myself. It is not possible to just copy your code) I hope there is something I can just specify the div and don't need to handle the image inside separately. Still, as the previous update, it is already quite good and I will award bounty if there are no better answer. – cytsunny Dec 30 '16 at 07:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/131878/discussion-between-addweb-solution-pvt-ltd-and-cytsunny). – AddWeb Solution Pvt Ltd Dec 30 '16 at 08:20
  • @cytsunny: I think its a high time to provide bounty.. 13 hours remaining :) – AddWeb Solution Pvt Ltd Jan 04 '17 at 14:48
5

onrendered is no longer working..

i solved this problem by adding "allowTaint" option

{ allowTaint: true }

2018 solution:

html2canvas(document.getElementById('result'), { allowTaint: true }).then(function (canvas) {
    document.body.appendChild(canvas);
});
r3mus
  • 59
  • 1
  • 1
2

You may try to scan the image source for external urls and change them to your website and load them with php.

Something like

$(function() { 

  var imageproxy = "http://example.com/imageproxy.php"; //Change this
  var mydomain = new RegExp(location.host);

  $("#btnSave").click(function() { 

    $("#widget").find('img').each(function(){

       var img_src = $(this).attr('src');
       if(!mydomain.test(img_src)){
           $(this).attr('src', imageproxy + "?url=" + encodeURIComponent(img_src));
       }
    });

    html2canvas($("#widget"), {
        onrendered: function(canvas) {

            var link = $('<a target="_blank">Download</a>');
            link.attr('download', 'test.png');
            link.attr('href', canvas.toDataURL());

            $("#btnSave").after(link);
        }
    });
  });
}); 

imageproxy.php

<?php

if(!empty($_GET['url'])){

    $url = urldecode($_GET['url']);
    $img_type = "image/jpeg";

    $chunks = explode('.', $url);

    if(is_array($chunks) && count($chunks) > 1){
        $ext = end($chunks);

        switch ($ext){

            case 'jpg':
            case 'jpeg':
                $img_type = "image/jpeg";
            break;

            case 'png':
                $img_type = "image/png";
            break;

            case 'gif':
                $img_type = "image/gif";
            break;
        }
    }


    $img = file_get_contents($url);

    header ("Content-Type: $img_type");
    echo $img;
}

note: php script is very simple, image detection is not working 100%, this is just ti give you an idea. If you use some php image libs to load and get image type you can do this just with few lines of code. you may also try with "php readfile".

sulest
  • 1,006
  • 1
  • 10
  • 17
  • I need a screenshot of the div, not only saving the images on div. – cytsunny Jan 05 '17 at 01:59
  • Did you test the code before voting down? in your question you say: "However, the external image is always replaced by a blank space." The solution I suggested fixes this issue and html2cavas is good enough to turn you div into screenshot – sulest Jan 05 '17 at 16:09