3

I need to test if a file is a JPEG or PNG and I can not trust the file extension because it might be wrong. To accomplish this I decided to use GIL.

Here's the header file declaring (and defining) boost::gil::png_read_dimensions. It clearly states that boost::gil::png_read_dimensions "Throws std::ios_base::failure if the location does not correspond to a valid PNG file". And indeed, this seems to be on par with the functions actual behaviour.

The problem is with boost::gil::jpeg_read_dimensions which you can see here. It also clearly states that it "Throws std::ios_base::failure if the location does not correspond to a valid JPEG file". However, this does not seem to be the case! No exception is thrown and instead libjpeg prints to stdout or stderr and then exits the program.

See the following code:

#include <iostream>
#include <string>

#include <boost/gil/extension/io/jpeg_io.hpp>
#include <boost/gil/extension/io/png_io.hpp>

using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::ios_base;

namespace bg = boost::gil;

int main(int, char**) {
  const string not_jpeg_or_png ("image.gif");

  cout << "bg::png_read_dimensions:" << endl;
  try {
    bg::png_read_dimensions(not_jpeg_or_png);
  } catch(const ios_base::failure &ib_f) {
    cerr << "what: " << ib_f.what() << endl;
  }

  cout << "\nbg::jpeg_read_dimensions:" << endl;
  try {
    bg::jpeg_read_dimensions(not_jpeg_or_png);
  } catch(const ios_base::failure &ib_f) {
    cerr << "what: " << ib_f.what() << endl;
  }

  cout << "\nDone." << endl;

  return 0;
}

Program output:

bg::png_read_dimensions:
what: png_check_validity: invalid png file: unspecified iostream_category error

bg::jpeg_read_dimensions:
Not a JPEG file: starts with 0x62 0x6c

Note how what: ... yadiyadiyada ... and Done. isn't printed.

I've tried doing } catch(...) { instead of } catch(const ios_base::failure &ib_f) { to make sure that no exception passes unnoticed but without success. No exception gets thrown!

Am I missing something really obvious here...? Am I doing something wrong? Is there a workaround?

sehe
  • 374,641
  • 47
  • 450
  • 633

2 Answers2

1

The easiest way is to read the first few bits of the file and see if they match the signature of the file type.

89 50 4E 47 0D 0A 1A 0A => PNG

FF D8 FF => JPEG

sehe
  • 374,641
  • 47
  • 450
  • 633
user3344003
  • 20,574
  • 3
  • 26
  • 62
  • I'll give it a go, thanks for the suggestion. "89 50 4E ..."; you've written it as hexadecimal pairs, right? –  May 15 '15 at 18:32
0

I don't understand why the exception as thrown apparently cannot be caught. It might be a bug.

However, to accomplish the task I'd suggest

  • libmagic or
  • identify (from ImageMagick)

which does what the other answer suggests.

This answer contains an example of how to use libmagic (which is usually present on linux, but can be built and (statically) linked on any other platform:

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • I'll give this question a day or two here on StackOverflow and if no satisfying answer has been given by then I'll see if I can post it to Boost Trac. If I do I'll make sure to post a link to the ticket here. –  May 15 '15 at 19:52
  • I opened a ticket [here](https://svn.boost.org/trac/boost/ticket/11314). I'm trying to look into it myself because I found this comment in the relevant code: _"// lbourdev: Can this return an error? You need to check and throw. Check all other library methods that can return an error state..."_... :) –  May 17 '15 at 18:05