├── test.png ├── .gitignore ├── .npmignore ├── test.js ├── README.md ├── package.json ├── LICENSE.md └── index.js /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hughsk/png-chunks-extract/HEAD/test.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const path = require('path') 3 | const Chunks = require('./') 4 | const fs = require('fs') 5 | 6 | test('png-chunks-extract', function (t) { 7 | const data = fs.readFileSync(path.join(__dirname, 'test.png')) 8 | 9 | t.deepEqual( 10 | Chunks(data), 11 | Chunks(new Uint8Array(data)), 12 | 'works identically with buffers and Uint8Arrays' 13 | ) 14 | 15 | const chunks = Chunks(data) 16 | const names = chunks.map(function (chunk) { 17 | return chunk.name 18 | }) 19 | 20 | const lengths = chunks.map(function (chunk) { 21 | return chunk.data.length 22 | }) 23 | 24 | t.deepEqual(lengths, [ 13, 3094, 9, 413, 16384, 16384, 16384, 7168, 0 ], 'extracted chunk lengths are as expected') 25 | t.deepEqual(names, [ 26 | 'IHDR', 27 | 'iCCP', 28 | 'pHYs', 29 | 'iTXt', 30 | 'IDAT', 31 | 'IDAT', 32 | 'IDAT', 33 | 'IDAT', 34 | 'IEND' 35 | ], 'extracted chunk names are as expected') 36 | 37 | t.end() 38 | }) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # png-chunks-extract 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Extract the data chunks from a PNG file. 6 | 7 | Useful for reading the metadata of a PNG image, or as the base of a more complete PNG parser. 8 | 9 | ## Usage 10 | 11 | [![NPM](https://nodei.co/npm/png-chunks-extract.png)](https://www.npmjs.com/package/png-chunks-extract) 12 | 13 | ### `chunks = extract(data)` 14 | 15 | Takes the raw image file `data` as a `Uint8Array` or Node.js `Buffer`, and returns an array of chunks. Each chunk has a name and data buffer: 16 | 17 | ``` javascript 18 | [ 19 | { name: 'IHDR', data: Uint8Array([...]) }, 20 | { name: 'IDAT', data: Uint8Array([...]) }, 21 | { name: 'IDAT', data: Uint8Array([...]) }, 22 | { name: 'IDAT', data: Uint8Array([...]) }, 23 | { name: 'IDAT', data: Uint8Array([...]) }, 24 | { name: 'IEND', data: Uint8Array([]) } 25 | ] 26 | ``` 27 | 28 | ## License 29 | 30 | MIT, see [LICENSE.md](http://github.com/hughsk/png-chunks-extract/blob/master/LICENSE.md) for details. 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "png-chunks-extract", 3 | "version": "1.0.0", 4 | "description": "Extract the data chunks from a PNG file", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Hugh Kennedy", 9 | "email": "hughskennedy@gmail.com", 10 | "url": "https://github.com/hughsk" 11 | }, 12 | "dependencies": { 13 | "crc-32": "^0.3.0" 14 | }, 15 | "devDependencies": { 16 | "tap-spec": "^4.1.0", 17 | "tape": "^4.2.0" 18 | }, 19 | "scripts": { 20 | "test": "node test | tap-spec" 21 | }, 22 | "keywords": [ 23 | "png,", 24 | "parse,", 25 | "extract,", 26 | "chunks,", 27 | "metadata,", 28 | "IHDR,", 29 | "image,", 30 | "hide,", 31 | "text,", 32 | "IEND,", 33 | "format,", 34 | "read" 35 | ], 36 | "repository": { 37 | "type": "git", 38 | "url": "git://github.com/hughsk/png-chunks-extract.git" 39 | }, 40 | "homepage": "https://github.com/hughsk/png-chunks-extract", 41 | "bugs": { 42 | "url": "https://github.com/hughsk/png-chunks-extract/issues" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Hugh Kennedy 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var crc32 = require('crc-32') 2 | 3 | module.exports = extractChunks 4 | 5 | // Used for fast-ish conversion between uint8s and uint32s/int32s. 6 | // Also required in order to remain agnostic for both Node Buffers and 7 | // Uint8Arrays. 8 | var uint8 = new Uint8Array(4) 9 | var int32 = new Int32Array(uint8.buffer) 10 | var uint32 = new Uint32Array(uint8.buffer) 11 | 12 | function extractChunks (data) { 13 | if (data[0] !== 0x89) throw new Error('Invalid .png file header') 14 | if (data[1] !== 0x50) throw new Error('Invalid .png file header') 15 | if (data[2] !== 0x4E) throw new Error('Invalid .png file header') 16 | if (data[3] !== 0x47) throw new Error('Invalid .png file header') 17 | if (data[4] !== 0x0D) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?') 18 | if (data[5] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?') 19 | if (data[6] !== 0x1A) throw new Error('Invalid .png file header') 20 | if (data[7] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?') 21 | 22 | var ended = false 23 | var chunks = [] 24 | var idx = 8 25 | 26 | while (idx < data.length) { 27 | // Read the length of the current chunk, 28 | // which is stored as a Uint32. 29 | uint8[3] = data[idx++] 30 | uint8[2] = data[idx++] 31 | uint8[1] = data[idx++] 32 | uint8[0] = data[idx++] 33 | 34 | // Chunk includes name/type for CRC check (see below). 35 | var length = uint32[0] + 4 36 | var chunk = new Uint8Array(length) 37 | chunk[0] = data[idx++] 38 | chunk[1] = data[idx++] 39 | chunk[2] = data[idx++] 40 | chunk[3] = data[idx++] 41 | 42 | // Get the name in ASCII for identification. 43 | var name = ( 44 | String.fromCharCode(chunk[0]) + 45 | String.fromCharCode(chunk[1]) + 46 | String.fromCharCode(chunk[2]) + 47 | String.fromCharCode(chunk[3]) 48 | ) 49 | 50 | // The IHDR header MUST come first. 51 | if (!chunks.length && name !== 'IHDR') { 52 | throw new Error('IHDR header missing') 53 | } 54 | 55 | // The IEND header marks the end of the file, 56 | // so on discovering it break out of the loop. 57 | if (name === 'IEND') { 58 | ended = true 59 | chunks.push({ 60 | name: name, 61 | data: new Uint8Array(0) 62 | }) 63 | 64 | break 65 | } 66 | 67 | // Read the contents of the chunk out of the main buffer. 68 | for (var i = 4; i < length; i++) { 69 | chunk[i] = data[idx++] 70 | } 71 | 72 | // Read out the CRC value for comparison. 73 | // It's stored as an Int32. 74 | uint8[3] = data[idx++] 75 | uint8[2] = data[idx++] 76 | uint8[1] = data[idx++] 77 | uint8[0] = data[idx++] 78 | 79 | var crcActual = int32[0] 80 | var crcExpect = crc32.buf(chunk) 81 | if (crcExpect !== crcActual) { 82 | throw new Error( 83 | 'CRC values for ' + name + ' header do not match, PNG file is likely corrupted' 84 | ) 85 | } 86 | 87 | // The chunk data is now copied to remove the 4 preceding 88 | // bytes used for the chunk name/type. 89 | var chunkData = new Uint8Array(chunk.buffer.slice(4)) 90 | 91 | chunks.push({ 92 | name: name, 93 | data: chunkData 94 | }) 95 | } 96 | 97 | if (!ended) { 98 | throw new Error('.png file ended prematurely: no IEND header was found') 99 | } 100 | 101 | return chunks 102 | } 103 | --------------------------------------------------------------------------------