Spaces:
Running
Running
//======================================================================== | |
// | |
// 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 <[email protected]> | |
// Copyright (C) 2018, 2021 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2019 Christian Persch <[email protected]> | |
// Copyright (C) 2019 LE GARREC Vincent <[email protected]> | |
// | |
// 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 | |
// | |
//======================================================================== | |
//------------------------------------------------------------------------ | |
namespace { // do not pollute global namespace | |
class Reader | |
{ | |
public: | |
Reader() = default; | |
Reader(const Reader &) = delete; | |
Reader &operator=(const Reader &other) = delete; | |
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 bool getU16BE(int pos, int *val) = 0; | |
// Read a big-endian unsigned 32-bit integer. Fills in *val and | |
// returns true if successful. | |
virtual bool getU32BE(int pos, unsigned int *val) = 0; | |
// Read a little-endian unsigned 32-bit integer. Fills in *val and | |
// returns true if successful. | |
virtual bool getU32LE(int pos, unsigned int *val) = 0; | |
// Read a big-endian unsigned <size>-byte integer, where 1 <= size | |
// <= 4. Fills in *val and returns true if successful. | |
virtual bool getUVarBE(int pos, int size, unsigned int *val) = 0; | |
// Compare against a string. Returns true if equal. | |
virtual bool cmp(int pos, const char *s) = 0; | |
}; | |
//------------------------------------------------------------------------ | |
class MemReader : public Reader | |
{ | |
public: | |
static MemReader *make(const char *bufA, int lenA); | |
~MemReader() override; | |
int getByte(int pos) override; | |
bool getU16BE(int pos, int *val) override; | |
bool getU32BE(int pos, unsigned int *val) override; | |
bool getU32LE(int pos, unsigned int *val) override; | |
bool getUVarBE(int pos, int size, unsigned int *val) override; | |
bool cmp(int pos, const char *s) override; | |
private: | |
MemReader(const char *bufA, int lenA); | |
const char *buf; | |
int len; | |
}; | |
MemReader *MemReader::make(const char *bufA, int lenA) | |
{ | |
return new MemReader(bufA, lenA); | |
} | |
MemReader::MemReader(const 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; | |
} | |
bool MemReader::getU16BE(int pos, int *val) | |
{ | |
if (pos < 0 || pos > len - 2) { | |
return false; | |
} | |
*val = ((buf[pos] & 0xff) << 8) + (buf[pos + 1] & 0xff); | |
return true; | |
} | |
bool MemReader::getU32BE(int pos, unsigned int *val) | |
{ | |
if (pos < 0 || pos > len - 4) { | |
return false; | |
} | |
*val = ((buf[pos] & 0xff) << 24) + ((buf[pos + 1] & 0xff) << 16) + ((buf[pos + 2] & 0xff) << 8) + (buf[pos + 3] & 0xff); | |
return true; | |
} | |
bool MemReader::getU32LE(int pos, unsigned int *val) | |
{ | |
if (pos < 0 || pos > len - 4) { | |
return false; | |
} | |
*val = (buf[pos] & 0xff) + ((buf[pos + 1] & 0xff) << 8) + ((buf[pos + 2] & 0xff) << 16) + ((buf[pos + 3] & 0xff) << 24); | |
return true; | |
} | |
bool MemReader::getUVarBE(int pos, int size, unsigned int *val) | |
{ | |
int i; | |
if (size < 1 || size > 4 || pos < 0 || pos > len - size) { | |
return false; | |
} | |
*val = 0; | |
for (i = 0; i < size; ++i) { | |
*val = (*val << 8) + (buf[pos + i] & 0xff); | |
} | |
return true; | |
} | |
bool MemReader::cmp(int pos, const char *s) | |
{ | |
int n; | |
n = (int)strlen(s); | |
if (pos < 0 || len < n || pos > len - n) { | |
return false; | |
} | |
return !memcmp(buf + pos, s, n); | |
} | |
//------------------------------------------------------------------------ | |
class FileReader : public Reader | |
{ | |
public: | |
static FileReader *make(const char *fileName); | |
~FileReader() override; | |
int getByte(int pos) override; | |
bool getU16BE(int pos, int *val) override; | |
bool getU32BE(int pos, unsigned int *val) override; | |
bool getU32LE(int pos, unsigned int *val) override; | |
bool getUVarBE(int pos, int size, unsigned int *val) override; | |
bool cmp(int pos, const char *s) override; | |
private: | |
explicit FileReader(FILE *fA); | |
bool fillBuf(int pos, int len); | |
FILE *f; | |
char buf[1024]; | |
int bufPos, bufLen; | |
}; | |
FileReader *FileReader::make(const char *fileName) | |
{ | |
FILE *fA; | |
if (!(fA = openFile(fileName, "rb"))) { | |
return nullptr; | |
} | |
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; | |
} | |
bool FileReader::getU16BE(int pos, int *val) | |
{ | |
if (!fillBuf(pos, 2)) { | |
return false; | |
} | |
*val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff); | |
return true; | |
} | |
bool FileReader::getU32BE(int pos, unsigned int *val) | |
{ | |
if (!fillBuf(pos, 4)) { | |
return false; | |
} | |
*val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff); | |
return true; | |
} | |
bool FileReader::getU32LE(int pos, unsigned int *val) | |
{ | |
if (!fillBuf(pos, 4)) { | |
return false; | |
} | |
*val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24); | |
return true; | |
} | |
bool FileReader::getUVarBE(int pos, int size, unsigned int *val) | |
{ | |
int i; | |
if (size < 1 || size > 4 || !fillBuf(pos, size)) { | |
return false; | |
} | |
*val = 0; | |
for (i = 0; i < size; ++i) { | |
*val = (*val << 8) + (buf[pos - bufPos + i] & 0xff); | |
} | |
return true; | |
} | |
bool FileReader::cmp(int pos, const char *s) | |
{ | |
int n; | |
n = (int)strlen(s); | |
if (!fillBuf(pos, n)) { | |
return false; | |
} | |
return !memcmp(buf - bufPos + pos, s, n); | |
} | |
bool FileReader::fillBuf(int pos, int len) | |
{ | |
if (pos < 0 || len < 0 || len > (int)sizeof(buf) || pos > INT_MAX - (int)sizeof(buf)) { | |
return false; | |
} | |
if (pos >= bufPos && pos + len <= bufPos + bufLen) { | |
return true; | |
} | |
if (fseek(f, pos, SEEK_SET)) { | |
return false; | |
} | |
bufPos = pos; | |
bufLen = (int)fread(buf, 1, sizeof(buf), f); | |
if (bufLen < len) { | |
return false; | |
} | |
return true; | |
} | |
//------------------------------------------------------------------------ | |
class StreamReader : public Reader | |
{ | |
public: | |
static StreamReader *make(int (*getCharA)(void *data), void *dataA); | |
~StreamReader() override; | |
int getByte(int pos) override; | |
bool getU16BE(int pos, int *val) override; | |
bool getU32BE(int pos, unsigned int *val) override; | |
bool getU32LE(int pos, unsigned int *val) override; | |
bool getUVarBE(int pos, int size, unsigned int *val) override; | |
bool cmp(int pos, const char *s) override; | |
private: | |
StreamReader(int (*getCharA)(void *data), void *dataA); | |
bool 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; | |
} | |
bool StreamReader::getU16BE(int pos, int *val) | |
{ | |
if (!fillBuf(pos, 2)) { | |
return false; | |
} | |
*val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff); | |
return true; | |
} | |
bool StreamReader::getU32BE(int pos, unsigned int *val) | |
{ | |
if (!fillBuf(pos, 4)) { | |
return false; | |
} | |
*val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff); | |
return true; | |
} | |
bool StreamReader::getU32LE(int pos, unsigned int *val) | |
{ | |
if (!fillBuf(pos, 4)) { | |
return false; | |
} | |
*val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24); | |
return true; | |
} | |
bool StreamReader::getUVarBE(int pos, int size, unsigned int *val) | |
{ | |
int i; | |
if (size < 1 || size > 4 || !fillBuf(pos, size)) { | |
return false; | |
} | |
*val = 0; | |
for (i = 0; i < size; ++i) { | |
*val = (*val << 8) + (buf[pos - bufPos + i] & 0xff); | |
} | |
return true; | |
} | |
bool StreamReader::cmp(int pos, const char *s) | |
{ | |
const int n = (int)strlen(s); | |
if (!fillBuf(pos, n)) { | |
return false; | |
} | |
const int posDiff = pos - bufPos; | |
return !memcmp(buf + posDiff, s, n); | |
} | |
bool StreamReader::fillBuf(int pos, int len) | |
{ | |
int c; | |
if (pos < 0 || len < 0 || len > (int)sizeof(buf) || pos > INT_MAX - (int)sizeof(buf)) { | |
return false; | |
} | |
if (pos < bufPos) { | |
return false; | |
} | |
// 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 false; | |
} | |
++bufPos; | |
} | |
} | |
} | |
// read the rest of the requested data | |
while (bufPos + bufLen < pos + len) { | |
if ((c = (*getChar)(data)) < 0) { | |
return false; | |
} | |
buf[bufLen++] = (char)c; | |
} | |
return true; | |
} | |
} | |
//------------------------------------------------------------------------ | |
static FoFiIdentifierType identify(Reader *reader); | |
static FoFiIdentifierType identifyOpenType(Reader *reader); | |
static FoFiIdentifierType identifyCFF(Reader *reader, int start); | |
FoFiIdentifierType FoFiIdentifier::identifyMem(const 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(const 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) | |
{ | |
unsigned int 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; | |
unsigned int 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 < (unsigned int)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) | |
{ | |
unsigned int 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 > (unsigned int)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 > (unsigned int)INT_MAX || !reader->getUVarBE(pos + 3 + offSize1, offSize1, &offset1) || offset1 > (unsigned int)INT_MAX || offset0 > offset1) { | |
return fofiIdUnknown; | |
} | |
if (checkedAdd(pos + 3 + (n + 1) * offSize1, (int)offset0 - 1, &pos) || checkedAdd(pos + 3 + (n + 1) * offSize1, (int)offset1 - 1, &endPos) || 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; | |
} | |
} | |