You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
9.3 KiB
408 lines
9.3 KiB
//========================================================================
|
|
//
|
|
// CMap.cc
|
|
//
|
|
// Copyright 2001-2003 Glyph & Cog, LLC
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "gmem.h"
|
|
#include "gfile.h"
|
|
#include "GString.h"
|
|
#include "Error.h"
|
|
#include "GlobalParams.h"
|
|
#include "PSTokenizer.h"
|
|
#include "CMap.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
struct CMapVectorEntry {
|
|
GBool isVector;
|
|
union {
|
|
CMapVectorEntry *vector;
|
|
CID cid;
|
|
};
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static int getCharFromFile(void *data) {
|
|
return fgetc((FILE *)data);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
CMap *CMap::parse(CMapCache *cache, GString *collectionA,
|
|
GString *cMapNameA) {
|
|
FILE *f;
|
|
CMap *cmap;
|
|
PSTokenizer *pst;
|
|
char tok1[256], tok2[256], tok3[256];
|
|
int n1, n2, n3;
|
|
Guint start, end, code;
|
|
|
|
if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
|
|
|
|
// Check for an identity CMap.
|
|
if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
|
|
return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
|
|
}
|
|
if (!cMapNameA->cmp("Identity-V")) {
|
|
return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
|
|
}
|
|
|
|
error(-1, "Couldn't find '%s' CMap file for '%s' collection",
|
|
cMapNameA->getCString(), collectionA->getCString());
|
|
return NULL;
|
|
}
|
|
|
|
cmap = new CMap(collectionA->copy(), cMapNameA->copy());
|
|
|
|
pst = new PSTokenizer(&getCharFromFile, f);
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
while (pst->getToken(tok2, sizeof(tok2), &n2)) {
|
|
if (!strcmp(tok2, "usecmap")) {
|
|
if (tok1[0] == '/') {
|
|
cmap->useCMap(cache, tok1 + 1);
|
|
}
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
} else if (!strcmp(tok1, "/WMode")) {
|
|
cmap->wMode = atoi(tok2);
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
} else if (!strcmp(tok2, "begincodespacerange")) {
|
|
while (pst->getToken(tok1, sizeof(tok1), &n1)) {
|
|
if (!strcmp(tok1, "endcodespacerange")) {
|
|
break;
|
|
}
|
|
if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
|
|
!strcmp(tok2, "endcodespacerange")) {
|
|
error(-1, "Illegal entry in codespacerange block in CMap");
|
|
break;
|
|
}
|
|
if (tok1[0] == '<' && tok2[0] == '<' &&
|
|
n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
|
|
tok1[n1 - 1] = tok2[n1 - 1] = '\0';
|
|
sscanf(tok1 + 1, "%x", &start);
|
|
sscanf(tok2 + 1, "%x", &end);
|
|
n1 = (n1 - 2) / 2;
|
|
cmap->addCodeSpace(cmap->vector, start, end, n1);
|
|
}
|
|
}
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
} else if (!strcmp(tok2, "begincidchar")) {
|
|
while (pst->getToken(tok1, sizeof(tok1), &n1)) {
|
|
if (!strcmp(tok1, "endcidchar")) {
|
|
break;
|
|
}
|
|
if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
|
|
!strcmp(tok2, "endcidchar")) {
|
|
error(-1, "Illegal entry in cidchar block in CMap");
|
|
break;
|
|
}
|
|
if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
|
|
n1 >= 4 && (n1 & 1) == 0)) {
|
|
error(-1, "Illegal entry in cidchar block in CMap");
|
|
continue;
|
|
}
|
|
tok1[n1 - 1] = '\0';
|
|
if (sscanf(tok1 + 1, "%x", &code) != 1) {
|
|
error(-1, "Illegal entry in cidchar block in CMap");
|
|
continue;
|
|
}
|
|
n1 = (n1 - 2) / 2;
|
|
cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
|
|
}
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
} else if (!strcmp(tok2, "begincidrange")) {
|
|
while (pst->getToken(tok1, sizeof(tok1), &n1)) {
|
|
if (!strcmp(tok1, "endcidrange")) {
|
|
break;
|
|
}
|
|
if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
|
|
!strcmp(tok2, "endcidrange") ||
|
|
!pst->getToken(tok3, sizeof(tok3), &n3) ||
|
|
!strcmp(tok3, "endcidrange")) {
|
|
error(-1, "Illegal entry in cidrange block in CMap");
|
|
break;
|
|
}
|
|
if (tok1[0] == '<' && tok2[0] == '<' &&
|
|
n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
|
|
tok1[n1 - 1] = tok2[n1 - 1] = '\0';
|
|
sscanf(tok1 + 1, "%x", &start);
|
|
sscanf(tok2 + 1, "%x", &end);
|
|
n1 = (n1 - 2) / 2;
|
|
cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
|
|
}
|
|
}
|
|
pst->getToken(tok1, sizeof(tok1), &n1);
|
|
} else {
|
|
strcpy(tok1, tok2);
|
|
}
|
|
}
|
|
delete pst;
|
|
|
|
fclose(f);
|
|
|
|
return cmap;
|
|
}
|
|
|
|
CMap::CMap(GString *collectionA, GString *cMapNameA) {
|
|
int i;
|
|
|
|
collection = collectionA;
|
|
cMapName = cMapNameA;
|
|
wMode = 0;
|
|
vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
|
|
for (i = 0; i < 256; ++i) {
|
|
vector[i].isVector = gFalse;
|
|
vector[i].cid = 0;
|
|
}
|
|
refCnt = 1;
|
|
#if MULTITHREADED
|
|
gInitMutex(&mutex);
|
|
#endif
|
|
}
|
|
|
|
CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
|
|
collection = collectionA;
|
|
cMapName = cMapNameA;
|
|
wMode = wModeA;
|
|
vector = NULL;
|
|
refCnt = 1;
|
|
#if MULTITHREADED
|
|
gInitMutex(&mutex);
|
|
#endif
|
|
}
|
|
|
|
void CMap::useCMap(CMapCache *cache, char *useName) {
|
|
GString *useNameStr;
|
|
CMap *subCMap;
|
|
|
|
useNameStr = new GString(useName);
|
|
subCMap = cache->getCMap(collection, useNameStr);
|
|
delete useNameStr;
|
|
if (!subCMap) {
|
|
return;
|
|
}
|
|
copyVector(vector, subCMap->vector);
|
|
subCMap->decRefCnt();
|
|
}
|
|
|
|
void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
|
|
int i, j;
|
|
|
|
for (i = 0; i < 256; ++i) {
|
|
if (src[i].isVector) {
|
|
if (!dest[i].isVector) {
|
|
dest[i].isVector = gTrue;
|
|
dest[i].vector =
|
|
(CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
|
|
for (j = 0; j < 256; ++j) {
|
|
dest[i].vector[j].isVector = gFalse;
|
|
dest[i].vector[j].cid = 0;
|
|
}
|
|
}
|
|
copyVector(dest[i].vector, src[i].vector);
|
|
} else {
|
|
if (dest[i].isVector) {
|
|
error(-1, "Collision in usecmap");
|
|
} else {
|
|
dest[i].cid = src[i].cid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
|
|
Guint nBytes) {
|
|
Guint start2, end2;
|
|
int startByte, endByte, i, j;
|
|
|
|
if (nBytes > 1) {
|
|
startByte = (start >> (8 * (nBytes - 1))) & 0xff;
|
|
endByte = (end >> (8 * (nBytes - 1))) & 0xff;
|
|
start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
|
|
end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
|
|
for (i = startByte; i <= endByte; ++i) {
|
|
if (!vec[i].isVector) {
|
|
vec[i].isVector = gTrue;
|
|
vec[i].vector =
|
|
(CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
|
|
for (j = 0; j < 256; ++j) {
|
|
vec[i].vector[j].isVector = gFalse;
|
|
vec[i].vector[j].cid = 0;
|
|
}
|
|
}
|
|
addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
|
|
CMapVectorEntry *vec;
|
|
CID cid;
|
|
int byte;
|
|
Guint i;
|
|
|
|
vec = vector;
|
|
for (i = nBytes - 1; i >= 1; --i) {
|
|
byte = (start >> (8 * i)) & 0xff;
|
|
if (!vec[byte].isVector) {
|
|
error(-1, "Invalid CID (%0*x - %0*x) in CMap",
|
|
2*nBytes, start, 2*nBytes, end);
|
|
return;
|
|
}
|
|
vec = vec[byte].vector;
|
|
}
|
|
cid = firstCID;
|
|
for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
|
|
if (vec[byte].isVector) {
|
|
error(-1, "Invalid CID (%0*x - %0*x) in CMap",
|
|
2*nBytes, start, 2*nBytes, end);
|
|
} else {
|
|
vec[byte].cid = cid;
|
|
}
|
|
++cid;
|
|
}
|
|
}
|
|
|
|
CMap::~CMap() {
|
|
delete collection;
|
|
delete cMapName;
|
|
if (vector) {
|
|
freeCMapVector(vector);
|
|
}
|
|
#if MULTITHREADED
|
|
gDestroyMutex(&mutex);
|
|
#endif
|
|
}
|
|
|
|
void CMap::freeCMapVector(CMapVectorEntry *vec) {
|
|
int i;
|
|
|
|
for (i = 0; i < 256; ++i) {
|
|
if (vec[i].isVector) {
|
|
freeCMapVector(vec[i].vector);
|
|
}
|
|
}
|
|
gfree(vec);
|
|
}
|
|
|
|
void CMap::incRefCnt() {
|
|
#if MULTITHREADED
|
|
gLockMutex(&mutex);
|
|
#endif
|
|
++refCnt;
|
|
#if MULTITHREADED
|
|
gUnlockMutex(&mutex);
|
|
#endif
|
|
}
|
|
|
|
void CMap::decRefCnt() {
|
|
GBool done;
|
|
|
|
#if MULTITHREADED
|
|
gLockMutex(&mutex);
|
|
#endif
|
|
done = --refCnt == 0;
|
|
#if MULTITHREADED
|
|
gUnlockMutex(&mutex);
|
|
#endif
|
|
if (done) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
GBool CMap::match(GString *collectionA, GString *cMapNameA) {
|
|
return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
|
|
}
|
|
|
|
CID CMap::getCID(char *s, int len, int *nUsed) {
|
|
CMapVectorEntry *vec;
|
|
int n, i;
|
|
|
|
if (!(vec = vector)) {
|
|
// identity CMap
|
|
*nUsed = 2;
|
|
if (len < 2) {
|
|
return 0;
|
|
}
|
|
return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
|
|
}
|
|
n = 0;
|
|
while (1) {
|
|
if (n >= len) {
|
|
*nUsed = n;
|
|
return 0;
|
|
}
|
|
i = s[n++] & 0xff;
|
|
if (!vec[i].isVector) {
|
|
*nUsed = n;
|
|
return vec[i].cid;
|
|
}
|
|
vec = vec[i].vector;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
CMapCache::CMapCache() {
|
|
int i;
|
|
|
|
for (i = 0; i < cMapCacheSize; ++i) {
|
|
cache[i] = NULL;
|
|
}
|
|
}
|
|
|
|
CMapCache::~CMapCache() {
|
|
int i;
|
|
|
|
for (i = 0; i < cMapCacheSize; ++i) {
|
|
if (cache[i]) {
|
|
cache[i]->decRefCnt();
|
|
}
|
|
}
|
|
}
|
|
|
|
CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
|
|
CMap *cmap;
|
|
int i, j;
|
|
|
|
if (cache[0] && cache[0]->match(collection, cMapName)) {
|
|
cache[0]->incRefCnt();
|
|
return cache[0];
|
|
}
|
|
for (i = 1; i < cMapCacheSize; ++i) {
|
|
if (cache[i] && cache[i]->match(collection, cMapName)) {
|
|
cmap = cache[i];
|
|
for (j = i; j >= 1; --j) {
|
|
cache[j] = cache[j - 1];
|
|
}
|
|
cache[0] = cmap;
|
|
cmap->incRefCnt();
|
|
return cmap;
|
|
}
|
|
}
|
|
if ((cmap = CMap::parse(this, collection, cMapName))) {
|
|
if (cache[cMapCacheSize - 1]) {
|
|
cache[cMapCacheSize - 1]->decRefCnt();
|
|
}
|
|
for (j = cMapCacheSize - 1; j >= 1; --j) {
|
|
cache[j] = cache[j - 1];
|
|
}
|
|
cache[0] = cmap;
|
|
cmap->incRefCnt();
|
|
return cmap;
|
|
}
|
|
return NULL;
|
|
}
|
|
|