text: //========================================================================
  //
  // FoFiIdentifier.cc
  //
  // Copyright 2009 Glyph & Cog, LLC
  //
  //========================================================================
  
  //========================================================================
  //
  // Modified under the Poppler project - http://poppler.freedesktop.org
  //
  // All changes made under the Poppler project to this file are licensed
  // under GPL version 2 or later
  //
  // Copyright (C) 2013 Christoph Duelli <duelli@melosgmbh.de>
  //
  // To see a description of the changes please see the Changelog file that
  // came with your tarball or type make ChangeLog if you are building from git
  //
  //========================================================================
  
  #ifdef USE_GCC_PRAGMAS
  #pragma implementation
  #endif
  
  #include <stdio.h>
  #include <string.h>
  #include <limits.h>
  #include "goo/gtypes.h"
  #include "FoFiIdentifier.h"
  
  //------------------------------------------------------------------------
  
  namespace { // do not pollute global namespace
  
  class Reader {
  public:
  
    virtual ~Reader() {}
  
    // Read one byte.  Returns -1 if past EOF.
    virtual int getByte(int pos) = 0;
  
    // Read a big-endian unsigned 16-bit integer.  Fills in *val and
    // returns true if successful.
    virtual GBool getU16BE(int pos, int *val) = 0;
  
    // Read a big-endian unsigned 32-bit integer.  Fills in *val and
    // returns true if successful.
    virtual GBool getU32BE(int pos, Guint *val) = 0;
  
    // Read a little-endian unsigned 32-bit integer.  Fills in *val and
    // returns true if successful.
    virtual GBool getU32LE(int pos, Guint *val) = 0;
  
    // Read a big-endian unsigned <size>-byte integer, where 1 <= size
    // <= 4.  Fills in *val and returns true if successful.
    virtual GBool getUVarBE(int pos, int size, Guint *val) = 0;
  
    // Compare against a string.  Returns true if equal.
    virtual GBool cmp(int pos, const char *s) = 0;
  
  };
  
  //------------------------------------------------------------------------
  
  class MemReader: public Reader {
  public:
  
    static MemReader *make(char *bufA, int lenA);
    virtual ~MemReader();
    virtual int getByte(int pos);
    virtual GBool getU16BE(int pos, int *val);
    virtual GBool getU32BE(int pos, Guint *val);
    virtual GBool getU32LE(int pos, Guint *val);
    virtual GBool getUVarBE(int pos, int size, Guint *val);
    virtual GBool cmp(int pos, const char *s);
  
  private:
  
    MemReader(char *bufA, int lenA);
  
    char *buf;
    int len;
  };
  
  MemReader *MemReader::make(char *bufA, int lenA) {
    return new MemReader(bufA, lenA);
  }
  
  MemReader::MemReader(char *bufA, int lenA) {
    buf = bufA;
    len = lenA;
  }
  
  MemReader::~MemReader() {
  }
  
  int MemReader::getByte(int pos) {
    if (pos < 0 || pos >= len) {
      return -1;
    }
    return buf[pos] & 0xff;
  }
  
  GBool MemReader::getU16BE(int pos, int *val) {
    if (pos < 0 || pos > len - 2) {
      return gFalse;
    }
    *val = ((buf[pos] & 0xff) << 8) +
           (buf[pos+1] & 0xff);
    return gTrue;
  }
  
  GBool MemReader::getU32BE(int pos, Guint *val) {
    if (pos < 0 || pos > len - 4) {
      return gFalse;
    }
    *val = ((buf[pos] & 0xff) << 24) +
           ((buf[pos+1] & 0xff) << 16) +
           ((buf[pos+2] & 0xff) << 8) +
           (buf[pos+3] & 0xff);
    return gTrue;
  }
  
  GBool MemReader::getU32LE(int pos, Guint *val) {
    if (pos < 0 || pos > len - 4) {
      return gFalse;
    }
    *val = (buf[pos] & 0xff) +
           ((buf[pos+1] & 0xff) << 8) +
           ((buf[pos+2] & 0xff) << 16) +
           ((buf[pos+3] & 0xff) << 24);
    return gTrue;
  }
  
  GBool MemReader::getUVarBE(int pos, int size, Guint *val) {
    int i;
  
    if (size < 1 || size > 4 || pos < 0 || pos > len - size) {
      return gFalse;
    }
    *val = 0;
    for (i = 0; i < size; ++i) {
      *val = (*val << 8) + (buf[pos + i] & 0xff);
    }
    return gTrue;
  }
  
  GBool MemReader::cmp(int pos, const char *s) {
    int n;
  
    n = (int)strlen(s);
    if (pos < 0 || len < n || pos > len - n) {
      return gFalse;
    }
    return !memcmp(buf + pos, s, n);
  }
  
  //------------------------------------------------------------------------
  
  class FileReader: public Reader {
  public:
  
    static FileReader *make(char *fileName);
    virtual ~FileReader();
    virtual int getByte(int pos);
    virtual GBool getU16BE(int pos, int *val);
    virtual GBool getU32BE(int pos, Guint *val);
    virtual GBool getU32LE(int pos, Guint *val);
    virtual GBool getUVarBE(int pos, int size, Guint *val);
    virtual GBool cmp(int pos, const char *s);
  
  private:
  
    FileReader(FILE *fA);
    GBool fillBuf(int pos, int len);
  
    FILE *f;
    char buf[1024];
    int bufPos, bufLen;
  };
  
  FileReader *FileReader::make(char *fileName) {
    FILE *fA;
  
    if (!(fA = fopen(fileName, "rb"))) {
      return NULL;
    }
    return new FileReader(fA);
  }
  
  FileReader::FileReader(FILE *fA) {
    f = fA;
    bufPos = 0;
    bufLen = 0;
  }
  
  FileReader::~FileReader() {
    fclose(f);
  }
  
  int FileReader::getByte(int pos) {
    if (!fillBuf(pos, 1)) {
      return -1;
    }
    return buf[pos - bufPos] & 0xff;
  }
  
  GBool FileReader::getU16BE(int pos, int *val) {
    if (!fillBuf(pos, 2)) {
      return gFalse;
    }
    *val = ((buf[pos - bufPos] & 0xff) << 8) +
           (buf[pos - bufPos + 1] & 0xff);
    return gTrue;
  }
  
  GBool FileReader::getU32BE(int pos, Guint *val) {
    if (!fillBuf(pos, 4)) {
      return gFalse;
    }
    *val = ((buf[pos - bufPos] & 0xff) << 24) +
           ((buf[pos - bufPos + 1] & 0xff) << 16) +
           ((buf[pos - bufPos + 2] & 0xff) << 8) +
           (buf[pos - bufPos + 3] & 0xff);
    return gTrue;
  }
  
  GBool FileReader::getU32LE(int pos, Guint *val) {
    if (!fillBuf(pos, 4)) {
      return gFalse;
    }
    *val = (buf[pos - bufPos] & 0xff) +
           ((buf[pos - bufPos + 1] & 0xff) << 8) +
           ((buf[pos - bufPos + 2] & 0xff) << 16) +
           ((buf[pos - bufPos + 3] & 0xff) << 24);
    return gTrue;
  }
  
  GBool FileReader::getUVarBE(int pos, int size, Guint *val) {
    int i;
  
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
      return gFalse;
    }
    *val = 0;
    for (i = 0; i < size; ++i) {
      *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
    }
    return gTrue;
  }
  
  GBool FileReader::cmp(int pos, const char *s) {
    int n;
  
    n = (int)strlen(s);
    if (!fillBuf(pos, n)) {
      return gFalse;
    }
    return !memcmp(buf - bufPos + pos, s, n);
  }
  
  GBool FileReader::fillBuf(int pos, int len) {
    if (pos < 0 || len < 0 || len > (int)sizeof(buf) ||
        pos > INT_MAX - (int)sizeof(buf)) {
      return gFalse;
    }
    if (pos >= bufPos && pos + len <= bufPos + bufLen) {
      return gTrue;
    }
    if (fseek(f, pos, SEEK_SET)) {
      return gFalse;
    }
    bufPos = pos;
    bufLen = (int)fread(buf, 1, sizeof(buf), f);
    if (bufLen < len) {
      return gFalse;
    }
    return gTrue;
  }
  
  //------------------------------------------------------------------------
  
  class StreamReader: public Reader {
  public:
  
    static StreamReader *make(int (*getCharA)(void *data), void *dataA);
    virtual ~StreamReader();
    virtual int getByte(int pos);
    virtual GBool getU16BE(int pos, int *val);
    virtual GBool getU32BE(int pos, Guint *val);
    virtual GBool getU32LE(int pos, Guint *val);
    virtual GBool getUVarBE(int pos, int size, Guint *val);
    virtual GBool cmp(int pos, const char *s);
  
  private:
  
    StreamReader(int (*getCharA)(void *data), void *dataA);
    GBool fillBuf(int pos, int len);
  
    int (*getChar)(void *data);
    void *data;
    int streamPos;
    char buf[1024];
    int bufPos, bufLen;
  };
  
  StreamReader *StreamReader::make(int (*getCharA)(void *data), void *dataA) {
    return new StreamReader(getCharA, dataA);
  }
  
  StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA) {
    getChar = getCharA;
    data = dataA;
    streamPos = 0;
    bufPos = 0;
    bufLen = 0;
  }
  
  StreamReader::~StreamReader() {
  }
  
  int StreamReader::getByte(int pos) {
    if (!fillBuf(pos, 1)) {
      return -1;
    }
    return buf[pos - bufPos] & 0xff;
  }
  
  GBool StreamReader::getU16BE(int pos, int *val) {
    if (!fillBuf(pos, 2)) {
      return gFalse;
    }
    *val = ((buf[pos - bufPos] & 0xff) << 8) +
           (buf[pos - bufPos + 1] & 0xff);
    return gTrue;
  }
  
  GBool StreamReader::getU32BE(int pos, Guint *val) {
    if (!fillBuf(pos, 4)) {
      return gFalse;
    }
    *val = ((buf[pos - bufPos] & 0xff) << 24) +
           ((buf[pos - bufPos + 1] & 0xff) << 16) +
           ((buf[pos - bufPos + 2] & 0xff) << 8) +
           (buf[pos - bufPos + 3] & 0xff);
    return gTrue;
  }
  
  GBool StreamReader::getU32LE(int pos, Guint *val) {
    if (!fillBuf(pos, 4)) {
      return gFalse;
    }
    *val = (buf[pos - bufPos] & 0xff) +
           ((buf[pos - bufPos + 1] & 0xff) << 8) +
           ((buf[pos - bufPos + 2] & 0xff) << 16) +
           ((buf[pos - bufPos + 3] & 0xff) << 24);
    return gTrue;
  }
  
  GBool StreamReader::getUVarBE(int pos, int size, Guint *val) {
    int i;
  
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
      return gFalse;
    }
    *val = 0;
    for (i = 0; i < size; ++i) {
      *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
    }
    return gTrue;
  }
  
  GBool StreamReader::cmp(int pos, const char *s) {
    int n;
  
    n = (int)strlen(s);
    if (!fillBuf(pos, n)) {
      return gFalse;
    }
    return !memcmp(buf - bufPos + pos, s, n);
  }
  
  GBool StreamReader::fillBuf(int pos, int len) {
    int c;
  
    if (pos < 0 || len < 0 || len > (int)sizeof(buf) ||
        pos > INT_MAX - (int)sizeof(buf)) {
      return gFalse;
    }
    if (pos < bufPos) {
      return gFalse;
    }
  
    // if requested region will not fit in the current buffer...
    if (pos + len > bufPos + (int)sizeof(buf)) {
  
      // if the start of the requested data is already in the buffer, move
      // it to the start of the buffer
      if (pos < bufPos + bufLen) {
        bufLen -= pos - bufPos;
        memmove(buf, buf + (pos - bufPos), bufLen);
        bufPos = pos;
  
      // otherwise discard data from the
      // stream until we get to the requested position
      } else {
        bufPos += bufLen;
        bufLen = 0;
        while (bufPos < pos) {
  	if ((c = (*getChar)(data)) < 0) {
  	  return gFalse;
  	}
  	++bufPos;
        }
      }
    }
  
    // read the rest of the requested data
    while (bufPos + bufLen < pos + len) {
      if ((c = (*getChar)(data)) < 0) {
        return gFalse;
      }
      buf[bufLen++] = (char)c;
    }
  
    return gTrue;
  }
  
  }
  
  //------------------------------------------------------------------------
  
  static FoFiIdentifierType identify(Reader *reader);
  static FoFiIdentifierType identifyOpenType(Reader *reader);
  static FoFiIdentifierType identifyCFF(Reader *reader, int start);
  
  FoFiIdentifierType FoFiIdentifier::identifyMem(char *file, int len) {
    MemReader *reader;
    FoFiIdentifierType type;
  
    if (!(reader = MemReader::make(file, len))) {
      return fofiIdError;
    }
    type = identify(reader);
    delete reader;
    return type;
  }
  
  FoFiIdentifierType FoFiIdentifier::identifyFile(char *fileName) {
    FileReader *reader;
    FoFiIdentifierType type;
  
    if (!(reader = FileReader::make(fileName))) {
      return fofiIdError;
    }
    type = identify(reader);
    delete reader;
    return type;
  }
  
  FoFiIdentifierType FoFiIdentifier::identifyStream(int (*getChar)(void *data),
  						  void *data) {
    StreamReader *reader;
    FoFiIdentifierType type;
  
    if (!(reader = StreamReader::make(getChar, data))) {
      return fofiIdError;
    }
    type = identify(reader);
    delete reader;
    return type;
  }
  
  static FoFiIdentifierType identify(Reader *reader) {
    Guint n;
  
    //----- PFA
    if (reader->cmp(0, "%!PS-AdobeFont-1") ||
        reader->cmp(0, "%!FontType1")) {
      return fofiIdType1PFA;
    }
  
    //----- PFB
    if (reader->getByte(0) == 0x80 &&
        reader->getByte(1) == 0x01 &&
        reader->getU32LE(2, &n)) {
      if ((n >= 16 && reader->cmp(6, "%!PS-AdobeFont-1")) ||
  	(n >= 11 && reader->cmp(6, "%!FontType1"))) {
        return fofiIdType1PFB;
      }
    }
  
    //----- TrueType
    if ((reader->getByte(0) == 0x00 &&
         reader->getByte(1) == 0x01 &&
         reader->getByte(2) == 0x00 &&
         reader->getByte(3) == 0x00) ||
        (reader->getByte(0) == 0x74 &&	// 'true'
         reader->getByte(1) == 0x72 &&
         reader->getByte(2) == 0x75 &&
         reader->getByte(3) == 0x65)) {
      return fofiIdTrueType;
    }
    if (reader->getByte(0) == 0x74 &&	// 'ttcf'
        reader->getByte(1) == 0x74 &&
        reader->getByte(2) == 0x63 &&
        reader->getByte(3) == 0x66) {
      return fofiIdTrueTypeCollection;
    }
  
    //----- OpenType
    if (reader->getByte(0) == 0x4f &&	// 'OTTO
        reader->getByte(1) == 0x54 &&
        reader->getByte(2) == 0x54 &&
        reader->getByte(3) == 0x4f) {
      return identifyOpenType(reader);
    }
  
    //----- CFF
    if (reader->getByte(0) == 0x01 &&
        reader->getByte(1) == 0x00) {
      return identifyCFF(reader, 0);
    }
    // some tools embed CFF fonts with an extra whitespace char at the
    // beginning
    if (reader->getByte(1) == 0x01 &&
        reader->getByte(2) == 0x00) {
      return identifyCFF(reader, 1);
    }
  
    return fofiIdUnknown;
  }
  
  static FoFiIdentifierType identifyOpenType(Reader *reader) {
    FoFiIdentifierType type;
    Guint offset;
    int nTables, i;
  
    if (!reader->getU16BE(4, &nTables)) {
      return fofiIdUnknown;
    }
    for (i = 0; i < nTables; ++i) {
      if (reader->cmp(12 + i*16, "CFF ")) {
        if (reader->getU32BE(12 + i*16 + 8, &offset) &&
  	  offset < (Guint)INT_MAX) {
  	type = identifyCFF(reader, (int)offset);
  	if (type == fofiIdCFF8Bit) {
  	  type = fofiIdOpenTypeCFF8Bit;
  	} else if (type == fofiIdCFFCID) {
  	  type = fofiIdOpenTypeCFFCID;
  	}
  	return type;
        }
        return fofiIdUnknown;
      }
    }
    return fofiIdUnknown;
  }
  
  static FoFiIdentifierType identifyCFF(Reader *reader, int start) {
    Guint offset0, offset1;
    int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i;
  
    //----- read the header
    if (reader->getByte(start) != 0x01 ||
        reader->getByte(start + 1) != 0x00) {
      return fofiIdUnknown;
    }
    if ((hdrSize = reader->getByte(start + 2)) < 0) {
      return fofiIdUnknown;
    }
    if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) {
      return fofiIdUnknown;
    }
    pos = start + hdrSize;
    if (pos < 0) {
      return fofiIdUnknown;
    }
  
    //----- skip the name index
    if (!reader->getU16BE(pos, &n)) {
      return fofiIdUnknown;
    }
    if (n == 0) {
      pos += 2;
    } else {
      if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
        return fofiIdUnknown;
      }
      if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) ||
  	offset1 > (Guint)INT_MAX) {
        return fofiIdUnknown;
      }
      pos += 3 + (n + 1) * offSize1 + (int)offset1 - 1;
    }
    if (pos < 0) {
      return fofiIdUnknown;
    }
  
    //----- parse the top dict index
    if (!reader->getU16BE(pos, &n) || n < 1) {
      return fofiIdUnknown;
    }
    if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
      return fofiIdUnknown;
    }
    if (!reader->getUVarBE(pos + 3, offSize1, &offset0) ||
        offset0 > (Guint)INT_MAX ||
        !reader->getUVarBE(pos + 3 + offSize1, offSize1, &offset1) ||
        offset1 > (Guint)INT_MAX ||
        offset0 > offset1) {
      return fofiIdUnknown;
    }
    pos = pos + 3 + (n + 1) * offSize1 + (int)offset0 - 1;
    endPos = pos + 3 + (n + 1) * offSize1 + (int)offset1 - 1;
    if (pos < 0 || endPos < 0 || pos > endPos) {
      return fofiIdUnknown;
    }
    
    //----- parse the top dict, look for ROS as first entry
    // for a CID font, the top dict starts with:
    //     <int> <int> <int> ROS
    for (i = 0; i < 3; ++i) {
      b0 = reader->getByte(pos++);
      if (b0 == 0x1c) {
        pos += 2;
      } else if (b0 == 0x1d) {
        pos += 4;
      } else if (b0 >= 0xf7 && b0 <= 0xfe) {
        pos += 1;
      } else if (b0 < 0x20 || b0 > 0xf6) {
        return fofiIdCFF8Bit;
      }
      if (pos >= endPos || pos < 0) {
        return fofiIdCFF8Bit;
      }
    }
    if (pos + 1 < endPos &&
        reader->getByte(pos) == 12 &&
        reader->getByte(pos + 1) == 30) {
      return fofiIdCFFCID;
    } else {
      return fofiIdCFF8Bit;
    }
  }
  