Created
February 10, 2014 16:30
-
-
Save sushihangover/8919188 to your computer and use it in GitHub Desktop.
Ometa/JS Parsing Example of a PNG (Binary) File
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ometa BinaryParser <: Parser { | |
// Portable Network Graphics (PNG) Specification (Second Edition) | |
// http://www.w3.org/TR/PNG/ | |
// Note: not all chunk are defined, this is just a POC | |
//entire PNG stream | |
START = [header:h (chunk+):c number*:n] -> [h,c,n], | |
//chunk definition | |
chunk = int4:len str4:t apply(t,len):d byte4:crc | |
-> [#chunk, [#type, t], [#length, len], [#data, d], [#crc, crc]], | |
//chunk types | |
IHDR :len = int4:w int4:h byte:dep byte:type byte:comp byte:filter byte:inter | |
-> {type:"IHDR", data:{width:w, height:h, bitdepth:dep, colortype:type, compression:comp, filter:filter, interlace:inter}}, | |
gAMA :len = int4:g -> {type:"gAMA", value:g}, | |
pHYs :len = int4:x int4:y byte:u -> {type:"pHYs", x:x, y:y, units:u}, | |
tEXt :len = repeat('byte',len):d -> {type:"tEXt", data:toAscii(d)}, | |
iTXt :len = repeat('byte',len):d -> {type:"iTXt", data:toShortAscii(d)}, | |
tIME :len = int2:y byte:mo byte:day byte:hr byte:min byte:sec | |
-> {type:"tIME", year:y, month:mo, day:day, hour:hr, minute:min, second:sec}, | |
IDAT :len = repeat('byte',len):d -> {type:"IDAT", data:"omitted"}, | |
IEND :len = repeat('byte',len):d -> {type:"IEND"}, | |
//useful definitions | |
byte = number, | |
header = 137 80 78 71 13 10 26 10 -> "PNG HEADER", //mandatory header | |
int2 = byte:a byte:b -> byteArrayToInt16([b,a]), //2 bytes to a 16bit integer | |
int4 = byte:a byte:b byte:c byte:d -> byteArrayToInt32([d,c,b,a]), //4 bytes to 32bit integer | |
str4 = byte:a byte:b byte:c byte:d -> toChunkType([a,b,c,d]), //4 byte string | |
byte4 = repeat('byte',4):d -> d, | |
END | |
} | |
BinaryParser.repeat = function(rule, count) { | |
var ret = []; | |
for(var i=0; i<count; i++) { | |
ret.push(this._apply(rule)); | |
} | |
return ret; | |
} | |
toAscii = function(byteArray) { | |
var foo = String.fromCharCode.apply(String, byteArray); | |
console.log ("String:" + foo + " (...byteArrayOmitted...)"); | |
return foo; | |
} | |
toShortAscii = function(byteArray) { | |
var embeddedText = String.fromCharCode.apply(String, byteArray); | |
// The iTxt chunk can contain a lot of text/xml, so truncate for proof of concept | |
console.log ("String:" + embeddedText.slice(1, 51) + " (...only first 50 bytes shown...)"); | |
return embeddedText; | |
} | |
toChunkType = function(byteArray) { | |
var aChuckType = String.fromCharCode.apply(String, byteArray); | |
console.log ("ChunkType :" + aChuckType + " : " + byteArray ); | |
return aChuckType; | |
} | |
byteArrayToInt32 = function(localByteArray) { | |
var uint8array = new Uint8Array(localByteArray); | |
var uint32array = new Uint32Array( | |
uint8array.buffer, | |
uint8array.byteOffset + uint8array.byteLength - 4, | |
1 // 4Bytes long | |
); | |
var newInt32 = uint32array[0]; | |
console.log ( "i32 : " + newInt32 + " <= " + localByteArray ); | |
return newInt32; | |
} | |
byteArrayToInt16 = function(byteArray) { | |
var ints = []; | |
alert(byteArray.length); | |
for (var i = 0; i < byteArray.length; i += 2) { | |
//ints.push((byteArray[i] << 8) | (byteArray[i+1])); | |
} | |
console.log (ints); | |
return ints; | |
} | |
fetchBinary = function() { | |
var req = new XMLHttpRequest(); | |
req.open("GET","http://sushihangover.azurewebsites.net/Content/Static/IronyLogoSmall.png",true); | |
req.responseType = "arraybuffer"; | |
req.onload = function(e) { | |
console.log("loaded"); | |
var buf = req.response; | |
if(buf) { | |
var byteArray = new Uint8Array(buf); | |
console.log("got " + byteArray.byteLength + " bytes"); | |
var arr = []; | |
for(var i=0; i<byteArray.byteLength; i++) { | |
arr.push(byteArray[i]); | |
} | |
// watch out if you uncomment the next line, it can kill your browser w/ large png files | |
// console.log(arr); | |
var parserResults = BinaryParser.match(arr, "START"); | |
console.log(parserResults); | |
} | |
} | |
req.send(null); | |
}; | |
fetchBinary(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment