foliojs / png.js Goto Github PK
View Code? Open in Web Editor NEWA (animated) PNG decoder in JavaScript for the HTML5 canvas element and Node.js
Home Page: http://devongovett.github.com/png.js
License: MIT License
A (animated) PNG decoder in JavaScript for the HTML5 canvas element and Node.js
Home Page: http://devongovett.github.com/png.js
License: MIT License
Lets summarize : in Hakuneko we use pdfkit to turn a serie of pictures into a pdf.
I discovered that the problem occurs in the pdf.js library used; when parsing tExt chunks
Apparently Druzai fixes is correcting the problem, since the error doesnt occurs anymore if i patch accordingly.
Corrupt files that start with a negative chunkSize get stuck in loop for a long time.
Hi,
I tried the decoding function in Node JS. When I loged, I got a Buffer instead of the expected array of pixels RGB values. How can I get an RGB array instead please.
var PNG = require("png-js"); PNG.decode("UVB-TEST.png", function (pixels) { // pixels is a 1d array (in rgba order) of decoded pixel data console.log(pixels); });
I would also know if there is a possibility to decode a function by providing its url instead of saving it in a local folder (because I need to process hundreds of images).
Thank you for your assistance.
Hi,
in png.js at line 135 you're incorrectly lengthening transparency.indexed to 255, if it's shorter.
The max length of that array should actually be the length of the palette divided by 3. And if it's parsed and is any longer there should be an error.
So it should read,
short = (this.palette.length/3) - this.transparency.indexed.length;
When throwing an error from inside zlib.inflate
's callback it goes right down to the event queue handlers, internal to node, and not to the user code. As this is uncaught it will crash the application even if only one image has a failure.
You can trap them with process.on('uncaughtException', ...)
to avoid the hard crash, but then fn()
never gets called and most use case programs will just keep waiting for a decode that never finishes, an unnecessary timeout.
Any error should either pass through to a callback function or be logged but never thrown from inside the decode coroutine.
Maybe a secondary optional reject parameter: decodePixels(fn, reject)
to avoid breaking the api?
Node v16.17.1
Lines:
https://github.com/foliojs/png.js/blob/master/png-node.js#L185
https://github.com/foliojs/png.js/blob/master/png-node.js#L293
MWE
const { inflate } = require('zlib');
// Same with inflate, gunzip, unzip
process.on('uncaughtException', err => {
console.error('Uncaught Error', err);
});
const buf = Buffer.alloc(1);
try {
inflate(buf, (error, decompressed) => {
throw new Error('See stack trace. Example usage: Found error / wrong data');
});
console.log('Continue');
}catch(e) {
console.error('Catch!', e);
}
Stack trace for the throw:
at Inflate.cb (/mwe.js:12:11)
at Inflate.zlibBufferOnError (node:zlib:146:8)
at Inflate.emit (node:events:513:28)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
Trying to add a "problematic" PNG into a pdf causes an unmanaged exception in our application because pdfKit doesn't give us any way to capture the exception.
The problem is in the asynchronous API internally used to decode PNGs. Look this code into png-node.js file:
decodePixels(fn) {
return zlib.inflate(this.imgData, (err, data) => {
...
switch (data[pos++]) {
....
default:
throw new Error(`Invalid filter algorithm: ${data[pos - 1]}`);
}
...
return fn(pixels);
}
}
An internal throw new Error("invalid filter..:")
is raised into the zlib.inflate callback causing NODE to capture it as an unmanaged exception.
Wrapping call into a try { } catch { } structure, doesn't help (because Inflate is called asynchronously )
try {
pdf.image(imageFile, ...)
} catch(e){
console.log("This message is not shown when an internal decode exception is raised");
}
In summary, calling image.decodePixels(function(data){...})
with problematic images can produce errors that are not "capturable" by the caller .
Only a comment: png-node.js API is asynchronows but without "error" management... that's really a bad library for a server side NODE application
you can find pieces of code like this:
PNG.decode = function(path, fn) {
return fs.readFile(path, function(err, file) {
var png;
png = new PNG(file);
return png.decode(function(pixels) {
return fn(pixels);
});
});
};
As you can see, the "err" parameter in the callback is not treated properly and, in fact, the "fn" parameter doesn't expect to be informed about possible errors.
Error: Invalid filter algorithm: 241
at /path/to/my/project/png-node.js:242:21
at Inflate.onEnd (zlib.js:227:5)
at emitNone (events.js:72:20)
at Inflate.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:921:12)
at nextTickCallbackWith2Args (node.js:442:9)
at process._tickCallback (node.js:356:17)
I found your LICENSE
file at top of the repo. However, there is no LICENSE
file in the release tarball (under https://registry.npmjs.org/). There should be.
Hi,
I'm using png-js to decode the png pics. When it comes to png with BGR (PNG files compressed by xcode), there would be error. Here is the error log.
path_to_project/node_modules/png-js/png-node.js:176
throw err;
^
Error: incorrect header check
at Zlib._binding.onerror (zlib.js:295:17)
I have no idea what to do.
The problem is that IDAT incorrectly coded!
APNG-8 does not display in PNG decoder.
Please FIX.
http://animatedpng.com/wp-content/uploads/clock.png
..........\node_modules\png-js\png-node.js:242
throw new Error("Invalid filter algorithm: " + data[pos - 1]);
^
Error: Invalid filter algorithm: 255
at Inflate.cb ( ........\node_modules\png-js\png-node.js:242:21)
at Inflate.zlibBufferOnEnd (node:zlib:161:10)
at Inflate.emit (node:events:520:28)
at endReadableNT (node:internal/streams/readable:1346:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
Hi, is it possible to decode PNG with your library without using HTML elements, CSS and SVG? Just converting numbers to numbers? And without network communication?
I think the only thing missing in png.js is converting PNG to iOS app and sending it ti App Store.
Since the library also detects corrupt files in terms of being incomplete, I'd expect it to also detect any corrupt images that have data succeeding IEND
and breaking image minification tools for example, e.g. grunt-contrib-imagemin with
Error: * Processing: app/images/layout/sticky_0.png
Warning: Extraneous data found after IEND
125x66 pixels, 4x8 bits/pixel, RGB+alpha
Reducing image to 2x8 bits/pixel, grayscale+alpha
Recoverable errors found in input. Rerun OptiPNG with -fix enabled.
Error: Previous error(s) not fixed
I don't see any reason for PNG.load()
to be synchronous. I understand it can be a desired behavior but why not make something like :
PNG.load = function(path,callback) {
var file;
if(!callback){
file = fs.readFileSync(path);
return new PNG(file);
}else{
file = fs.readFile(path,function(err,file){
if(err) callback(err);
return callback(null,new PNG(file));
});
}
};
Of course it's an improvisation and there may be cleaner ways to achieve it, but I think the core idea is useful.
My use case is : I'm only interested in text tags so PNG.decode
is a no-go, but I still want async operations.
I have been finding solution to convert cross origin image to data url until I found this open source project. I thought this was my solution. But turns out cross origin image only work in png.js, not in jpg.js
what is happening behind?
It's not a good idea to install npm modules using sudo
. I'd recommend you don't advertise this method in your README.
Please choose an opensource license:
http://choosealicense.com/
The callback function for png.decode should have two arguments. The first should be for handling an error, the second should be the image data.
As it stands, if an image is loaded for a file which does not exist, the cryptic crash happens:
TypeError: Cannot read property '8' of undefined
Hi can you add the license field in the package.json.
"license": "MIT"
https://docs.npmjs.com/cli/v9/configuring-npm/package-json#license
We have an automatically license checker that is failing without this information.
Thank you very much.
Is it possible to load a 16-bits per channel PNG and get a Uint16Array of the actual source data?
I am trying to use this from a Web Worker in a browser.
I get the image data by doing xhr with responseType arraybuffer. I then do var p = new PNG(arrbuf);
However this gets stuck into an infinite loop. I see this is because:
while (true) {
chunkSize = this.readUInt32();
section = ((function() {
var _i, _results;
_results = [];
for (i = _i = 0; _i < 4; i = ++_i) {
_results.push(String.fromCharCode(this.data[this.pos++]));
}
return _results;
}).call(this)).join('');
Can you please help me modify to use in Web Worker scope.
Thanks!
Hi,
I was trying to render a 16-bit png but doesn't seem like it supports 16 bit. I do that both "bits" and "pixelBitlength" were set to 16.
Any idea?
Thanks!
Hi, the package.json file has no license and the is no LICENSE or COPYING file present in the repo so it seems this project is not really open-source right now.
Is this intentional or just an oversight?
Hi,
I would like to use client-side png decoder with angular, installed png-js, and imported as:
import * PNG from png-js'
but when I examine in browser PNG it is coming from png-node.js. How can I tell it to use png.js (used for client side)?
**Note sorry for sending this as an issue (obviously it is not) but I couldn't find a forum to ask my question.
Thanks.
I want to load in some very small images with indexed colour, in the browser. I don't actually want to display the image, only read the colours of the pixels, but documentation is a little thin, and I'm only as far as using the example code to load the image into a canvas element and it's throwing an "invalid arguments" error.
Breaking as the error is thrown shows it's at png.js line 231:
pixels = new Uint8Array(scanlineLength * this.height);
because scanlineLength is set to 7.5.
That was set a few lines earlier, because width is 15 and pixelBytes is 0.5, and pixelBytes is 0.5 because pixelBitLength is 4 and this is divided by 8.
Sample image is at http://tremby.net/dump/finn.png
I check optimized APNG. Result: DON'T DISPLAYED.
http://animatedpng.com/wp-content/uploads/clock.png
This buggy image in png.js.
I am convinced that with the palette is not!
False alarm, my code was wrong.
I've this snippet reading a png
<?php
$file_name = './ReactNative-snapshot-image6513693026486043647.png';
$image_file_content = base64_encode(file_get_contents( $file_name ));
$image_size = getimagesize( $file_name );
?>
I need to convert the png to a Uint8ClampedArray (to decode a QRCode client-side, long story)
Is there a way using your lib to get file content from a base64 string?
For instance, I am doing a similar thing using jpeg-js
const jpegData = Buffer.from("<?php echo $image_file_content ?>", 'base64');
const rawImageData = jpeg.decode(jpegData);
const clamped_array = Uint8ClampedArray.from(rawImageData.data);
const decodedGreenpass = window.jsQR(
clamped_array,
<?php echo $image_size[0]; ?>,
<?php echo $image_size[1]; ?>
);
I need to obtain in some way pngData or a Uint8ClampedArray
Thanks a ton for png.js! I'm loving it for handling ARDrone video.
It would be awesome if it were bidirectional as well - PNG.encode(rgbaData, width, height);
and png.save('filename.png');
I want to send a stream of PNGs up to the browser after piping them through some processing.
I can try to implement this, but I'm a bit intimidated by http://www.w3.org/TR/PNG/ . Any help you (or anyone reading this) can give would be much appreciated.
I wanted to make sure this works, so I didn't try to fiddle with png-node.coffee, I modified the .js output.
PNG.prototype.decodePixels = function(fn) {
var _this = this;
return zlib.inflate(this.imgData, function(err, data) {
if (err) throw err;
var pixelBytes = _this.pixelBitlength / 8;
var fullPixels = new Buffer(_this.width * _this.height * pixelBytes);
var pos = 0;
function pass(x0, y0, dx, dy) {
var byte, c, col, i, left, p, pa, paeth, pb, pc, pixels, row, scanlineLength, upper, upperLeft;
var w = Math.ceil((_this.width - x0) / dx), h = Math.ceil((_this.height - y0) / dy);
var isFull = _this.width == w && _this.height == h;
scanlineLength = pixelBytes * w;
pixels = isFull ? fullPixels : new Buffer(scanlineLength * h);
row = 0;
c = 0;
while (row < h && pos < data.length) {
switch (data[pos++]) {
case 0:
for (i = 0; i < scanlineLength; i += 1) {
pixels[c++] = data[pos++];
}
break;
case 1:
for (i = 0; i < scanlineLength; i += 1) {
byte = data[pos++];
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
pixels[c++] = (byte + left) % 256;
}
break;
case 2:
for (i = 0; i < scanlineLength; i += 1) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)];
pixels[c++] = (upper + byte) % 256;
}
break;
case 3:
for (i = 0; i < scanlineLength; i += 1) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)];
pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256;
}
break;
case 4:
for (i = 0; i < scanlineLength; i += 1) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
if (row === 0) {
upper = upperLeft = 0;
} else {
upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)];
upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)];
}
p = left + upper - upperLeft;
pa = Math.abs(p - left);
pb = Math.abs(p - upper);
pc = Math.abs(p - upperLeft);
if (pa <= pb && pa <= pc) {
paeth = left;
} else if (pb <= pc) {
paeth = upper;
} else {
paeth = upperLeft;
}
pixels[c++] = (byte + paeth) % 256;
}
break;
default:
throw new Error("Invalid filter algorithm: " + data[pos - 1]);
}
if (!isFull) {
var fullPos = ((y0 + row * dy) * _this.width + x0) * pixelBytes;
var partPos = row * scanlineLength;
for (i = 0; i < w; i += 1) {
for (var j = 0; j < pixelBytes; j += 1)
fullPixels[fullPos++] = pixels[partPos++];
fullPos += (dx - 1) * pixelBytes;
}
}
row++;
}
}
if (_this.interlaceMethod == 1) {
/*
1 6 4 6 2 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
3 6 4 6 3 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
*/
pass(0, 0, 8, 8); // 1
/* NOTE these seem to follow the pattern:
* pass(x, 0, 2*x, 2*x);
* pass(0, x, x, 2*x);
* with x being 4, 2, 1.
*/
pass(4, 0, 8, 8); // 2
pass(0, 4, 4, 8); // 3
pass(2, 0, 4, 4); // 4
pass(0, 2, 2, 4); // 5
pass(1, 0, 2, 2); // 6
pass(0, 1, 1, 2); // 7
} else
pass(0, 0, 1, 1);
return fn(fullPixels);
});
};
I don't seem to be able to get png-js to load PNGs made in Windows 8.1's Paint on Linux or Windows 8.1.
Repro source:
var PNG = require("png-js")
var png = new PNG("test.png")
Expected behaviour: completes without any errors or visible side effects.
Actual behaviour: crashes while constructing the PNG with the following error:
C:\Users\jameswilddev\Desktop\node_modules\png-js\png-node.js:140
throw new Error("Incomplete or corrupt PNG file");
^
Error: Incomplete or corrupt PNG file
at new PNG (C:\Users\jameswilddev\Desktop\node_modules\png-js\png-node.js:14
0:17)
at Object. (C:\Users\jameswilddev\Desktop\repro\repro.js:2:11)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
Environment details:
Windows 8.1 x64
Node.js v0.10.33
png-js 0.1.1
Ubuntu Linux 14.04.1 LTS (Trusty)
Node.js v0.10.35
png-js 0.1.1
I can open this image without a problem in Paint, Chrome or GIMP. Have attached but don't know if GitHub will do any pre-processing on it which will fix it, so I've also uploaded it to my Cloud9 at:
https://sunruse-jameswilddev.c9.io/test.png
Thank you for reading.
I have problem when i use png image. But i found code to fix this problem. please change code to
PNG.prototype.decodePixels = function(fn) { var _this = this; return zlib.inflate(this.imgData, function(err, data) { if (err) throw err; var pixelBytes = _this.pixelBitlength / 8; var fullPixels = new Buffer(_this.width * _this.height * pixelBytes); var pos = 0; function pass(x0, y0, dx, dy) { var byte, c, col, i, left, p, pa, paeth, pb, pc, pixels, row, scanlineLength, upper, upperLeft; var w = Math.ceil((_this.width - x0) / dx), h = Math.ceil((_this.height - y0) / dy); var isFull = _this.width == w && _this.height == h; scanlineLength = pixelBytes * w; pixels = isFull ? fullPixels : new Buffer(scanlineLength * h); row = 0; c = 0; while (row < h && pos < data.length) { switch (data[pos++]) { case 0: for (i = 0; i < scanlineLength; i += 1) { pixels[c++] = data[pos++]; } break; case 1: for (i = 0; i < scanlineLength; i += 1) { byte = data[pos++]; left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; pixels[c++] = (byte + left) % 256; } break; case 2: for (i = 0; i < scanlineLength; i += 1) { byte = data[pos++]; col = (i - (i % pixelBytes)) / pixelBytes; upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; pixels[c++] = (upper + byte) % 256; } break; case 3: for (i = 0; i < scanlineLength; i += 1) { byte = data[pos++]; col = (i - (i % pixelBytes)) / pixelBytes; left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256; } break; case 4: for (i = 0; i < scanlineLength; i += 1) { byte = data[pos++]; col = (i - (i % pixelBytes)) / pixelBytes; left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; if (row === 0) { upper = upperLeft = 0; } else { upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)]; } p = left + upper - upperLeft; pa = Math.abs(p - left); pb = Math.abs(p - upper); pc = Math.abs(p - upperLeft); if (pa <= pb && pa <= pc) { paeth = left; } else if (pb <= pc) { paeth = upper; } else { paeth = upperLeft; } pixels[c++] = (byte + paeth) % 256; } break; default: throw new Error("Invalid filter algorithm: " + data[pos - 1]); } if (!isFull) { var fullPos = ((y0 + row * dy) * _this.width + x0) * pixelBytes; var partPos = row * scanlineLength; for (i = 0; i < w; i += 1) { for (var j = 0; j < pixelBytes; j += 1) fullPixels[fullPos++] = pixels[partPos++]; fullPos += (dx - 1) * pixelBytes; } } row++; } } if (_this.interlaceMethod == 1) { /* 1 6 4 6 2 6 4 6 7 7 7 7 7 7 7 7 5 6 5 6 5 6 5 6 7 7 7 7 7 7 7 7 3 6 4 6 3 6 4 6 7 7 7 7 7 7 7 7 5 6 5 6 5 6 5 6 7 7 7 7 7 7 7 7 */ pass(0, 0, 8, 8); // 1 /* NOTE these seem to follow the pattern: * pass(x, 0, 2*x, 2*x); * pass(0, x, x, 2*x); * with x being 4, 2, 1. */ pass(4, 0, 8, 8); // 2 pass(0, 4, 4, 8); // 3 pass(2, 0, 4, 4); // 4 pass(0, 2, 2, 4); // 5 pass(1, 0, 2, 2); // 6 pass(0, 1, 1, 2); // 7 } else pass(0, 0, 1, 1); return fn(fullPixels); }); };
on file png-node.js #8
Hi
Doing something in this manner (CoffeeScript, can provide compiled version)
image_url = "/images/ninja.png"
PNG.load image_url, null, (png) ->
png.decode (pixels) ->
console.log pixels
Fails here:
data = imageData.data;
length = data.length; // Cannot read property 'length' of undefined
in .copyToImageData
.
The README states pixels is a 1d array of decoded pixel data
but does not mention what the ordering is. Is this the same as a canvas image data object, with [r,g,b,a] pattern for each screen pixel? (if so, can the README be amended with that information? if not: same request =)
Hi @devongovett !
On my current effort of getting rid of your libraries's forks on react-pdf (textkit, unicode-properties, pdfkit, fontkit and this lib), I think it would be beneficial for everybody to make this library a bit more browser friendly.
These are some of the issues I currently have with png.js:
fs
, which is the one that pdfkit uses. So I cant use pdfkit without adding shimswindow
Changes proposal:
fs
on web build and (maybe) adding the XMLHttpRequest
instead. Maybe adding the animation decoding also to this core (fcTL, fdAT sections)? There's no issues with zlib
and Buffer
since bundlers will shim them automatically (at least as far as I saw)/png.js
with all the canvas manipulation for the people who is already using itI already have a react-pdf fork with some of these points, and it's working great. These types of changes will make one day possible for me to rely directly on the main packages, and actively contribute to those. It's getting very complicated for me to do so when I have to also work on the forks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.