source: trunk/poppler/mypoppler/poppler/CMap.cc @ 461

Last change on this file since 461 was 277, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.3

File size: 10.9 KB
Line 
1//========================================================================
2//
3// CMap.cc
4//
5// Copyright 2001-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2008 Koji Otani <sho@bbr.jp>
17// Copyright (C) 2008, 2009 Albert Astals Cid <aacid@kde.org>
18//
19// To see a description of the changes please see the Changelog file that
20// came with your tarball or type make ChangeLog if you are building from git
21//
22//========================================================================
23
24#include <config.h>
25
26#ifdef USE_GCC_PRAGMAS
27#pragma implementation
28#endif
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <ctype.h>
34#include "goo/gmem.h"
35#include "goo/gfile.h"
36#include "goo/GooString.h"
37#include "Error.h"
38#include "GlobalParams.h"
39#include "PSTokenizer.h"
40#include "CMap.h"
41#include "Object.h"
42
43//------------------------------------------------------------------------
44
45struct CMapVectorEntry {
46  GBool isVector;
47  union {
48    CMapVectorEntry *vector;
49    CID cid;
50  };
51};
52
53//------------------------------------------------------------------------
54
55static int getCharFromFile(void *data) {
56  return fgetc((FILE *)data);
57}
58
59static int getCharFromStream(void *data) {
60  return ((Stream *)data)->getChar();
61}
62
63//------------------------------------------------------------------------
64
65CMap *CMap::parse(CMapCache *cache, GooString *collectionA,
66                  GooString *cMapNameA, Stream *stream) {
67  FILE *f = NULL;
68  CMap *cmap;
69  PSTokenizer *pst;
70  char tok1[256], tok2[256], tok3[256];
71  int n1, n2, n3;
72  Guint start, end, code;
73
74  if (stream) {
75    stream->reset();
76    pst = new PSTokenizer(&getCharFromStream, stream);
77  } else {
78    if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
79
80      // Check for an identity CMap.
81      if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
82        return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
83      }
84      if (!cMapNameA->cmp("Identity-V")) {
85        return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
86      }
87
88      error(-1, "Couldn't find '%s' CMap file for '%s' collection",
89            cMapNameA->getCString(), collectionA->getCString());
90      return NULL;
91    }
92    pst = new PSTokenizer(&getCharFromFile, f);
93  }
94
95  cmap = new CMap(collectionA->copy(), cMapNameA->copy());
96
97  pst->getToken(tok1, sizeof(tok1), &n1);
98  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
99    if (!strcmp(tok2, "usecmap")) {
100      if (tok1[0] == '/') {
101        cmap->useCMap(cache, tok1 + 1);
102      }
103      pst->getToken(tok1, sizeof(tok1), &n1);
104    } else if (!strcmp(tok1, "/WMode")) {
105      cmap->wMode = atoi(tok2);
106      pst->getToken(tok1, sizeof(tok1), &n1);
107    } else if (!strcmp(tok2, "begincodespacerange")) {
108      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
109        if (!strcmp(tok1, "endcodespacerange")) {
110          break;
111        }
112        if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
113            !strcmp(tok2, "endcodespacerange")) {
114          error(-1, "Illegal entry in codespacerange block in CMap");
115          break;
116        }
117        if (tok1[0] == '<' && tok2[0] == '<' &&
118            n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
119          tok1[n1 - 1] = tok2[n1 - 1] = '\0';
120          sscanf(tok1 + 1, "%x", &start);
121          sscanf(tok2 + 1, "%x", &end);
122          n1 = (n1 - 2) / 2;
123          cmap->addCodeSpace(cmap->vector, start, end, n1);
124        }
125      }
126      pst->getToken(tok1, sizeof(tok1), &n1);
127    } else if (!strcmp(tok2, "begincidchar")) {
128      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
129        if (!strcmp(tok1, "endcidchar")) {
130          break;
131        }
132        if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
133            !strcmp(tok2, "endcidchar")) {
134          error(-1, "Illegal entry in cidchar block in CMap");
135          break;
136        }
137        if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
138              n1 >= 4 && (n1 & 1) == 0)) {
139          error(-1, "Illegal entry in cidchar block in CMap");
140          continue;
141        }
142        tok1[n1 - 1] = '\0';
143        if (sscanf(tok1 + 1, "%x", &code) != 1) {
144          error(-1, "Illegal entry in cidchar block in CMap");
145          continue;
146        }
147        n1 = (n1 - 2) / 2;
148        cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
149      }
150      pst->getToken(tok1, sizeof(tok1), &n1);
151    } else if (!strcmp(tok2, "begincidrange")) {
152      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
153        if (!strcmp(tok1, "endcidrange")) {
154          break;
155        }
156        if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
157            !strcmp(tok2, "endcidrange") ||
158            !pst->getToken(tok3, sizeof(tok3), &n3) ||
159            !strcmp(tok3, "endcidrange")) {
160          error(-1, "Illegal entry in cidrange block in CMap");
161          break;
162        }
163        if (tok1[0] == '<' && tok2[0] == '<' &&
164            n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
165          tok1[n1 - 1] = tok2[n1 - 1] = '\0';
166          sscanf(tok1 + 1, "%x", &start);
167          sscanf(tok2 + 1, "%x", &end);
168          n1 = (n1 - 2) / 2;
169          cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
170        }
171      }
172      pst->getToken(tok1, sizeof(tok1), &n1);
173    } else {
174      strcpy(tok1, tok2);
175    }
176  }
177  delete pst;
178
179  if (f) {
180    fclose(f);
181  }
182
183  return cmap;
184}
185
186CMap::CMap(GooString *collectionA, GooString *cMapNameA) {
187  int i;
188
189  collection = collectionA;
190  cMapName = cMapNameA;
191  wMode = 0;
192  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
193  for (i = 0; i < 256; ++i) {
194    vector[i].isVector = gFalse;
195    vector[i].cid = 0;
196  }
197  refCnt = 1;
198#if MULTITHREADED
199  gInitMutex(&mutex);
200#endif
201}
202
203CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) {
204  collection = collectionA;
205  cMapName = cMapNameA;
206  wMode = wModeA;
207  vector = NULL;
208  refCnt = 1;
209#if MULTITHREADED
210  gInitMutex(&mutex);
211#endif
212}
213
214void CMap::useCMap(CMapCache *cache, char *useName) {
215  GooString *useNameStr;
216  CMap *subCMap;
217
218  useNameStr = new GooString(useName);
219  subCMap = cache->getCMap(collection, useNameStr, NULL);
220  delete useNameStr;
221  if (!subCMap) {
222    return;
223  }
224  copyVector(vector, subCMap->vector);
225  subCMap->decRefCnt();
226}
227
228void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
229  int i, j;
230
231  for (i = 0; i < 256; ++i) {
232    if (src[i].isVector) {
233      if (!dest[i].isVector) {
234        dest[i].isVector = gTrue;
235        dest[i].vector =
236          (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
237        for (j = 0; j < 256; ++j) {
238          dest[i].vector[j].isVector = gFalse;
239          dest[i].vector[j].cid = 0;
240        }
241      }
242      copyVector(dest[i].vector, src[i].vector);
243    } else {
244      if (dest[i].isVector) {
245        error(-1, "Collision in usecmap");
246      } else {
247        dest[i].cid = src[i].cid;
248      }
249    }
250  }
251}
252
253void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
254                        Guint nBytes) {
255  Guint start2, end2;
256  int startByte, endByte, i, j;
257
258  if (nBytes > 1) {
259    startByte = (start >> (8 * (nBytes - 1))) & 0xff;
260    endByte = (end >> (8 * (nBytes - 1))) & 0xff;
261    start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
262    end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
263    for (i = startByte; i <= endByte; ++i) {
264      if (!vec[i].isVector) {
265        vec[i].isVector = gTrue;
266        vec[i].vector =
267          (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
268        for (j = 0; j < 256; ++j) {
269          vec[i].vector[j].isVector = gFalse;
270          vec[i].vector[j].cid = 0;
271        }
272      }
273      addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
274    }
275  }
276}
277
278void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
279  CMapVectorEntry *vec;
280  CID cid;
281  int byte;
282  Guint i;
283
284  vec = vector;
285  for (i = nBytes - 1; i >= 1; --i) {
286    byte = (start >> (8 * i)) & 0xff;
287    if (!vec[byte].isVector) {
288      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
289            2*nBytes, start, 2*nBytes, end);
290      return;
291    }
292    vec = vec[byte].vector;
293  }
294  cid = firstCID;
295  for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
296    if (vec[byte].isVector) {
297      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
298            2*nBytes, start, 2*nBytes, end);
299    } else {
300      vec[byte].cid = cid;
301    }
302    ++cid;
303  }
304}
305
306CMap::~CMap() {
307  delete collection;
308  delete cMapName;
309  if (vector) {
310    freeCMapVector(vector);
311  }
312#if MULTITHREADED
313  gDestroyMutex(&mutex);
314#endif
315}
316
317void CMap::freeCMapVector(CMapVectorEntry *vec) {
318  int i;
319
320  for (i = 0; i < 256; ++i) {
321    if (vec[i].isVector) {
322      freeCMapVector(vec[i].vector);
323    }
324  }
325  gfree(vec);
326}
327
328void CMap::incRefCnt() {
329#if MULTITHREADED
330  gLockMutex(&mutex);
331#endif
332  ++refCnt;
333#if MULTITHREADED
334  gUnlockMutex(&mutex);
335#endif
336}
337
338void CMap::decRefCnt() {
339  GBool done;
340
341#if MULTITHREADED
342  gLockMutex(&mutex);
343#endif
344  done = --refCnt == 0;
345#if MULTITHREADED
346  gUnlockMutex(&mutex);
347#endif
348  if (done) {
349    delete this;
350  }
351}
352
353GBool CMap::match(GooString *collectionA, GooString *cMapNameA) {
354  return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
355}
356
357CID CMap::getCID(char *s, int len, int *nUsed) {
358  CMapVectorEntry *vec;
359  int n, i;
360
361  if (!(vec = vector)) {
362    // identity CMap
363    *nUsed = 2;
364    if (len < 2) {
365      return 0;
366    }
367    return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
368  }
369  n = 0;
370  while (1) {
371    if (n >= len) {
372      *nUsed = n;
373      return 0;
374    }
375    i = s[n++] & 0xff;
376    if (!vec[i].isVector) {
377      *nUsed = n;
378      return vec[i].cid;
379    }
380    vec = vec[i].vector;
381  }
382}
383
384void CMap::setReverseMapVector(Guint startCode, CMapVectorEntry *vec,
385 Guint *rmap, Guint rmapSize, Guint ncand) {
386  int i;
387
388  if (vec == 0) return;
389  for (i = 0;i < 256;i++) {
390    if (vec[i].isVector) {
391      setReverseMapVector((startCode+i) << 8,
392          vec[i].vector,rmap,rmapSize,ncand);
393    } else {
394      Guint cid = vec[i].cid;
395
396      if (cid < rmapSize) {
397        Guint cand;
398
399        for (cand = 0;cand < ncand;cand++) {
400          Guint code = startCode+i;
401          Guint idx = cid*ncand+cand;
402          if (rmap[idx] == 0) {
403            rmap[idx] = code;
404            break;
405          } else if (rmap[idx] == code) {
406            break;
407          }
408        }
409      }
410    }
411  }
412}
413
414void CMap::setReverseMap(Guint *rmap, Guint rmapSize, Guint ncand) {
415  setReverseMapVector(0,vector,rmap,rmapSize,ncand);
416}
417
418//------------------------------------------------------------------------
419
420CMapCache::CMapCache() {
421  int i;
422
423  for (i = 0; i < cMapCacheSize; ++i) {
424    cache[i] = NULL;
425  }
426}
427
428CMapCache::~CMapCache() {
429  int i;
430
431  for (i = 0; i < cMapCacheSize; ++i) {
432    if (cache[i]) {
433      cache[i]->decRefCnt();
434    }
435  }
436}
437
438CMap *CMapCache::getCMap(GooString *collection, GooString *cMapName, Stream *stream) {
439  CMap *cmap;
440  int i, j;
441
442  if (cache[0] && cache[0]->match(collection, cMapName)) {
443    cache[0]->incRefCnt();
444    return cache[0];
445  }
446  for (i = 1; i < cMapCacheSize; ++i) {
447    if (cache[i] && cache[i]->match(collection, cMapName)) {
448      cmap = cache[i];
449      for (j = i; j >= 1; --j) {
450        cache[j] = cache[j - 1];
451      }
452      cache[0] = cmap;
453      cmap->incRefCnt();
454      return cmap;
455    }
456  }
457  if ((cmap = CMap::parse(this, collection, cMapName, stream))) {
458    if (cache[cMapCacheSize - 1]) {
459      cache[cMapCacheSize - 1]->decRefCnt();
460    }
461    for (j = cMapCacheSize - 1; j >= 1; --j) {
462      cache[j] = cache[j - 1];
463    }
464    cache[0] = cmap;
465    cmap->incRefCnt();
466    return cmap;
467  }
468  return NULL;
469}
Note: See TracBrowser for help on using the repository browser.