I have a need to find the dimensions of images online without downloading them. To accomplish this I do this:
+ (CGSize) getImageDimensions:(NSString *)url {
// Send a synchronous request
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: url]];
NSString *rangeString = [url hasSuffix: @"png"] ? @"bytes=0-100" : @"bytes=0-1300";
[urlRequest setValue:rangeString forHTTPHeaderField:@"Range"];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error == nil)
return [UIImage imageWithData: data].size;
else
return CGSizeMake(0, 0);
}
This (downloading the first 100 bytes) surprisingly works and I get the correct dimensions for PNGs this way.
However I do not think this is a very elegant approach. First, I chose to download the first 100 bytes by just guess and checking, making it as small as possible whilst still having it work okay.
Apparently in a PNG file there's this thing called a IHDR in the header and I have to find it and directly after it are the width and height. This gives me the impression that I should loop over the data and find this IHDR and get the dimensions. The problem is, when I NSLog the data I get something like this:
... 49484452 000003b7 000001a7 08060000 006c2da0 b100000a 41694343 50494343 2050726f 66696c65 ...
I have no idea how to handle looping over my NSData object and detecting the IHDR token and then converting the things that come after it to numbers. I also have no idea if requesting just 100 bytes for a PNG is requesting too much just to get the dimensions, or if it's requesting not enough
Rationale
According to the PNG specification:
So you have to read those to make sure you really have a PNG file.
Then,
So according to the structure of chunks, you first have to read the four bytes representing the length of the chunk's data field, then the four bytes representing the chunk's name, then the two 32-bit integers representing width and height, eight bytes in total.
So, the exact minimal number of bytes you must read in order to determine width and height is 8 + 4 + 4 + 8 = 24 bytes.
Objective-C code
Once you have your NSData object, you simply have to access the bytes that it contains:
Optionally, but I recommend it strongly, check that you indeed have a PNG file:
Make sure you have an IHDR:
Width and height can be accessed as big-endian encoded unsigned ints at offset 24 minus 8 and minus 4 bytes respectively:
Edit: Fixed buffer reading code: PNG actually stores integers in big-endian.