Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PNG with cHRM chunk fails to render #122

Closed
kangax opened this issue Oct 15, 2011 · 29 comments
Closed

PNG with cHRM chunk fails to render #122

kangax opened this issue Oct 15, 2011 · 29 comments

Comments

@kangax
Copy link
Collaborator

kangax commented Oct 15, 2011

Here's a simplified test case, where one image renders and another one fails (url vs url2). Both images open fine in a browser (and can be downloaded and viewed locally).

Image fails with "Error: Image given has not completed loaded".

I'm not 100% sure if the problem is with node-canvas. Would appreciate any pointers!

var Canvas = require('canvas')
  , Image = Canvas.Image
  , canvas = new Canvas(500, 500)
  , ctx = canvas.getContext('2d')
  , fs = require('fs')
  , URL = require('url')
  , HTTP = require('http')
  , url = 'http://s3-eu-west-1.amazonaws.com/printio/assets/images/large/9c6af4fc958a1967eaa4dba1200fef75ca2f68e9.png'
  , url2 = 'https://www.google.com/intl/en_com/images/srpr/logo3w.png';

function request(url, encoding, callback) {
  var oURL = URL.parse(url),
      client = HTTP.createClient(80, oURL.hostname),
      request = client.request('GET', oURL.pathname, { 'host': oURL.hostname });

  request.end();
  request.on('response', function (response) {
    var body = "";
    if (encoding) {
      response.setEncoding(encoding);
    }
    response.on('end', function () {
      callback(body);
    });
    response.on('data', function (chunk) {
      if (response.statusCode == 200) {
        body += chunk;
      }
    });
  });
}

request(url2, 'binary', function(body) {
  var img = new Image();
  img.src = new Buffer(body, 'binary');

  ctx.drawImage(img, 0, 0);

  var out = fs.createWriteStream(__dirname + '/output.png')
    , stream = canvas.createPNGStream();

  stream.on('data', function(chunk){
    out.write(chunk);
  });
});
@tj
Copy link
Contributor

tj commented Oct 15, 2011

interesting! because neither should work actually haha, currently only local IO works for Image#src= it wont do an http request

@kangax
Copy link
Collaborator Author

kangax commented Oct 16, 2011

But I'm assigning a buffer to Image#src, not expecting any requests to be made : )

I'm sending request manually via request function (defined in this test case), and create a string as the response body comes in (body += chunk in response.on('data', ...)). That string is then passed to new Buffer (with "binary" as a second argument) and that's what gets assigned to Image#src.

I thought this was supposed to work?

@tj
Copy link
Contributor

tj commented Oct 16, 2011

oh wow haha, fail on my part, i should have read the code. Yeah buffers should definitely work, if I have a minute I'll try and reproduce this

@kangax
Copy link
Collaborator Author

kangax commented Oct 16, 2011

Thanks, TJ!

@tj
Copy link
Contributor

tj commented Oct 16, 2011

hmm definitely seems ok for the quick test I did:

var Canvas = require('../')
  , Image = Canvas.Image
  , canvas = new Canvas(400, 400)
  , ctx = canvas.getContext('2d')
  , http = require('http')
  , parse = require('url').parse
  , fs = require('fs');


var url = parse('https://assets0.github.com/img/89d8e6624fb9153c40bd11ae7592a74e058d873e?repo=&url=http%3A%2F%2Fsphotos.ak.fbcdn.net%2Fhphotos-ak-snc3%2Fhs234.snc3%2F22173_446973930292_559060292_10921426_7238463_n.jpg&path=');

http.get({
    path: url.pathname + url.search
  , host: url.hostname
}, function(res){
  var buf = '';
  res.setEncoding('binary');
  res.on('data', function(chunk){ buf += chunk });
  res.on('end', function(){
    var img = new Image;
    img.src = new Buffer(buf, 'binary');
    ctx.drawImage(img, 0, 0);
    fs.writeFile('/tmp/tobi.png', canvas.toBuffer());
  });
});

@kangax
Copy link
Collaborator Author

kangax commented Oct 16, 2011

Yep, that works.

But my test image still doesn't work when plugged into your test case :(

It's almost as if something is wrong with an image, but I can't tell what.

> curl -I http://s3-eu-west-1.amazonaws.com/printio/assets/images/large/9c6af4fc958a1967eaa4dba1200fef75ca2f68e9.png

HTTP/1.1 200 OK
x-amz-id-2: y7NjZSaxlBgWGbh5bpOdHBtCQYpNnn6H3VGNNJqK7cVXmmTkeiZR8GQ7c21bhwkx
x-amz-request-id: 5DCA5C20C3531FA0
Date: Sun, 16 Oct 2011 22:17:03 GMT
Expires: Tue, 18 Sep 2012 22:34:11 GMT
Last-Modified: Thu, 22 Sep 2011 11:35:00 GMT
ETag: "90c4ac4d7b6c5cac73d5ded1bae1c768"
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 57586
Server: AmazonS3

It opens fine in the browser, and I can save and view it locally. It also draws on canvas in a browser perfectly:

<canvas id="c" width="500" height="500"></canvas>
<script>
  var canvas = document.getElementById('c'),
      ctx = canvas.getContext('2d'),
      img = new Image;

  img.onload = function() {
    ctx.drawImage(img, 0, 0);
  };
  img.src = 'http://s3-eu-west-1.amazonaws.com/printio/assets/images/large/9c6af4fc958a1967eaa4dba1200fef75ca2f68e9.png';
</script>

Any ideas?

@kangax
Copy link
Collaborator Author

kangax commented Oct 16, 2011

FWIW, this isn't the only image that fails. We get similar problem with other images occasionally.

@tj
Copy link
Contributor

tj commented Oct 16, 2011

hmmmm it could be a few things, tough call but perhaps my magic number stuff is off, I'll grab that image and see if I can figure out what's going on

@tj
Copy link
Contributor

tj commented Oct 16, 2011

ah, it actually gets an onerror callback haha. I've been meaning to throw that when no error handler is present, the browser api is kinda weird IMO, but what browser APIs are not weird haha. I'll see if I can find what's not parsing correctly

@tj
Copy link
Contributor

tj commented Oct 16, 2011

hmm when I request with curl or the browser I get the image, however with node I get a 403 and some xml

@tj
Copy link
Contributor

tj commented Oct 16, 2011

getting this funky stderr from libpng:

wx=0.312700, wy=0.312700, rx=0.640000, ry=0.330000
gx=0.300000, gy=0.600000, bx=0.150000, by=0.060000

@kangax
Copy link
Collaborator Author

kangax commented Oct 16, 2011

We just found that all images that fail were processed via ImageMagick to have their white background removed. Might have something to do with it. Investigating further...

@tj
Copy link
Contributor

tj commented Oct 16, 2011

seems to be here in png_handle_cHRM http://cpansearch.perl.org/src/NI-S/Tk-PNG-2.005/libpng/pngrutil.c

@giuliandrimba
Copy link

I am having the same issue. I tried to run one of the examples and it didn't work.

This codes:

var fs = require("fs")
var Image = require('canvas').Image
var Canvas = require('canvas')
, canvas = new Canvas(200,200)
, ctx = canvas.getContext('2d');

fs.readFile(__dirname + '/teste.png', function(err, squid){
if (err) throw err;
img = new Image;
img.src = squid;
ctx.drawImage(img, 0, 0, img.width / 4, img.height / 4);
});

Throws the error:

ctx.drawImage(img, 0, 0, img.width / 4, img.height / 4);
^
Error: Image given has not completed loading

Any solution? I am in a Mac OSX 10.6.8.

Thanks,

Giulian Drimba.

@gonzoramos
Copy link

Has anybody find a resolution for this issue? I can reproduce the problem when using the following code

var Canvas = require('canvas')
, Image = Canvas.Image
, canvas = new Canvas(400, 400)
, ctx = canvas.getContext('2d')
, http = require('http')
, parse = require('url').parse
, fs = require('fs');

var url = parse('http://www.kaplaninternational.com/Images/01-Seattle-skyline_tcm7-15463.jpg');

http.get({
path: url.pathname + url.search
, host: url.hostname
}, function(res){
var buf = '';
res.setEncoding('binary');
res.on('data', function(chunk){ buf += chunk });
res.on('end', function(){
var img = new Image;
img.src = new Buffer(buf, 'binary');

ctx.drawImage(img, 0, 0);
fs.writeFile('./test.jpg', canvas.toBuffer());      

});
});

I get

ctx.drawImage(img, 0, 0);
    ^

Error: Image given has not completed loading

Any help would be greatly appreciated!

@giuliandrimba
Copy link

No..in my case, I decided to use https://github.com/hacksparrow/node-easyimage instead.

@buschtoens
Copy link

@giuliandrimba Uhhh, ImageMagic is disgusting! All you can do is some cropping and that not even well.
Does your image work with this example code?

@gonzoramos Have you even tried to log your path? This can't work, because parse does not return search in your case. So what you're requesting is /Images/01-Seattle-skyline_tcm7-15463.jpgundefined, which of course throws a 404.

@kaxline
Copy link

kaxline commented Feb 23, 2013

was there any resolution to this? i just upgraded to 1.0.0 and the exception is still being thrown every once in a while for a stray image. i'm trying to log the images to see if i can find a common thread between them, but i was hoping someone had found a systemic fix.

thanks!

@gonzoramos
Copy link

I found that if I use the following pattern, things work...

var img = new Image();
img.onload( function() {
// ... do things
} );
img.src = new Buffer ( data, 'binary');

@mattmcla
Copy link

This is still an issue for me. It's as though the onload is never called. I'm using a buffer with an svg and trying to convert it to a png.

@jiguang
Copy link

jiguang commented Nov 17, 2013

I got the same problem, I want to access RGB data of an image, is node-canvas the only option?

@sritify
Copy link

sritify commented Nov 28, 2013

I search for many solutions but no one works... anyone can correct on the source and release a possible package to use?
I think the major problem is the image size is larger then what node-canvas can carry

@shevchenhe
Copy link

I also got this problem ,when my image size is small, everything is ok, but it failed when the image size became large.....

Could any one figure it out?

@jonerer
Copy link

jonerer commented Jan 30, 2014

+1 getting this for all images...

@choyongjoon
Copy link

It worked successfully with this code. (I used 204x204 jpg file)

http.get(url, function (res) {
    var buf = '';
    res.setEncoding('binary');
    res.on('data', function(chunk){ buf += chunk; });
    res.on('end', function(){
        var canvas = new Canvas();
        var image = new Canvas.Image;
        image.onload = function(){
            console.log('onload');
        };
        image.onerror = function(err){
            console.log(err);
        };
        image.src = new Buffer(buf, 'binary');
    });
});

My experience:

  1. Write image.onload and image.onerror before image.src
  2. image.onError -> image.onerror
  3. If you get this error at onload
[Error: error while reading from input stream]

You should check your libjpeg, libpng. Uninstall node-canvas, install them and reinstall node-canvas.

I hope this works to you.

@pkayfire
Copy link

was facing the same error and for some reason reinstalling node-canvas worked!

@missdora
Copy link

missdora commented Apr 8, 2015

@choyongjoon It works for me! thank you!

@zbjornson
Copy link
Collaborator

The cHRM chunk in http://s3-eu-west-1.amazonaws.com/printio/assets/images/large/9c6af4fc958a1967eaa4dba1200fef75ca2f68e9.png is causing the failure. I think we need to use libpng directly instead of cairo's png functions (which it says are toys). That would let us fix #73 as well (which is due to a gAMA chunk bomb).

@zbjornson zbjornson changed the title Image fails to render PNG with cHRM chunk fails to render Jul 21, 2018
@zbjornson
Copy link
Collaborator

This works with Cairo 1.16.0 🎉 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests