source: trunk/poppler/mypoppler/poppler/CairoFontEngine.cc @ 257

Last change on this file since 257 was 257, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.0

File size: 14.3 KB
Line 
1//========================================================================
2//
3// CairoFontEngine.cc
4//
5// Copyright 2003 Glyph & Cog, LLC
6// Copyright 2004 Red Hat, Inc
7//
8//========================================================================
9
10//========================================================================
11//
12// Modified under the Poppler project - http://poppler.freedesktop.org
13//
14// All changes made under the Poppler project to this file are licensed
15// under GPL version 2 or later
16//
17// Copyright (C) 2005-2007 Jeff Muizelaar <jeff@infidigm.net>
18// Copyright (C) 2005, 2006 Kristian HÞgsberg <krh@redhat.com>
19// Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org>
20// Copyright (C) 2005 Albert Astals Cid <aacid@kde.org>
21// Copyright (C) 2006, 2007 Carlos Garcia Campos <carlosgc@gnome.org>
22// Copyright (C) 2007 Koji Otani <sho@bbr.jp>
23// Copyright (C) 2008 Chris Wilson <chris@chris-wilson.co.uk>
24//
25// To see a description of the changes please see the Changelog file that
26// came with your tarball or type make ChangeLog if you are building from git
27//
28//========================================================================
29
30#include <config.h>
31
32#include "config.h"
33#include <string.h>
34#include "CairoFontEngine.h"
35#include "CharCodeToUnicode.h"
36#include "GlobalParams.h"
37#include <fofi/FoFiTrueType.h>
38#include <fofi/FoFiType1C.h>
39#include "goo/gfile.h"
40#include "Error.h"
41
42#if HAVE_FCNTL_H && HAVE_SYS_MMAN_H && HAVE_SYS_STAT_H
43#include <fcntl.h>
44#include <sys/stat.h>
45#include <sys/mman.h>
46#define CAN_CHECK_OPEN_FACES 1
47#endif
48
49#ifdef USE_GCC_PRAGMAS
50#pragma implementation
51#endif
52
53static void fileWrite(void *stream, char *data, int len) {
54  fwrite(data, 1, len, (FILE *)stream);
55}
56
57//------------------------------------------------------------------------
58// CairoFont
59//------------------------------------------------------------------------
60
61static cairo_user_data_key_t _ft_cairo_key;
62
63static void
64_ft_done_face_uncached (void *closure)
65{
66    FT_Face face = (FT_Face) closure;
67    FT_Done_Face (face);
68}
69
70static GBool
71_ft_new_face_uncached (FT_Library lib,
72                       const char *filename,
73                       FT_Face *face_out,
74                       cairo_font_face_t **font_face_out)
75{
76  FT_Face face;
77  cairo_font_face_t *font_face;
78
79  if (FT_New_Face (lib, filename, 0, &face))
80    return gFalse;
81
82  font_face = cairo_ft_font_face_create_for_ft_face (face,
83                                                          FT_LOAD_NO_HINTING |
84                                                          FT_LOAD_NO_BITMAP);
85  if (cairo_font_face_set_user_data (font_face,
86                                     &_ft_cairo_key,
87                                     face,
88                                     _ft_done_face_uncached))
89  {
90    _ft_done_face_uncached (face);
91    cairo_font_face_destroy (font_face);
92    return gFalse;
93  }
94
95  *face_out = face;
96  *font_face_out = font_face;
97  return gTrue;
98}
99
100#if CAN_CHECK_OPEN_FACES
101static struct _ft_face_data {
102  struct _ft_face_data *prev, *next, **head;
103
104  int fd;
105  unsigned long hash;
106  size_t size;
107  unsigned char *bytes;
108
109  FT_Library lib;
110  FT_Face face;
111  cairo_font_face_t *font_face;
112} *_ft_open_faces;
113
114static unsigned long
115_djb_hash (const unsigned char *bytes, size_t len)
116{
117  unsigned long hash = 5381;
118  while (len--) {
119    unsigned char c = *bytes++;
120    hash *= 33;
121    hash ^= c;
122  }
123  return hash;
124}
125
126static GBool
127_ft_face_data_equal (struct _ft_face_data *a, struct _ft_face_data *b)
128{
129  if (a->lib != b->lib)
130    return gFalse;
131  if (a->size != b->size)
132    return gFalse;
133  if (a->hash != b->hash)
134    return gFalse;
135
136  return memcmp (a->bytes, b->bytes, a->size) == 0;
137}
138
139static void
140_ft_done_face (void *closure)
141{
142  struct _ft_face_data *data = (struct _ft_face_data *) closure;
143
144  if (data->next)
145    data->next->prev = data->prev;
146  if (data->prev)
147    data->prev->next = data->next;
148  else
149    _ft_open_faces = data->next;
150
151  munmap (data->bytes, data->size);
152  close (data->fd);
153
154  FT_Done_Face (data->face);
155  gfree (data);
156}
157
158static GBool
159_ft_new_face (FT_Library lib,
160              const char *filename,
161              FT_Face *face_out,
162              cairo_font_face_t **font_face_out)
163{
164  struct _ft_face_data *l;
165  struct stat st;
166  struct _ft_face_data tmpl;
167
168  /* if we fail to mmap the file, just pass it to FreeType instead */
169  tmpl.fd = open (filename, O_RDONLY);
170  if (tmpl.fd == -1)
171    return _ft_new_face_uncached (lib, filename, face_out, font_face_out);
172
173  if (fstat (tmpl.fd, &st) == -1) {
174    close (tmpl.fd);
175    return _ft_new_face_uncached (lib, filename, face_out, font_face_out);
176  }
177
178  tmpl.bytes = (unsigned char *) mmap (NULL, st.st_size,
179                                       PROT_READ, MAP_PRIVATE,
180                                       tmpl.fd, 0);
181  if (tmpl.bytes == MAP_FAILED) {
182    close (tmpl.fd);
183    return _ft_new_face_uncached (lib, filename, face_out, font_face_out);
184  }
185
186  /* check to see if this is a duplicate of any of the currently open fonts */
187  tmpl.lib = lib;
188  tmpl.size = st.st_size;
189  tmpl.hash = _djb_hash (tmpl.bytes, tmpl.size);
190
191  for (l = _ft_open_faces; l; l = l->next) {
192    if (_ft_face_data_equal (l, &tmpl)) {
193      munmap (tmpl.bytes, tmpl.size);
194      close (tmpl.fd);
195      *face_out = l->face;
196      *font_face_out = cairo_font_face_reference (l->font_face);
197      return gTrue;
198    }
199  }
200
201  /* not a dup, open and insert into list */
202  if (FT_New_Face (lib, filename, 0, &tmpl.face)) {
203    munmap (tmpl.bytes, tmpl.size);
204    close (tmpl.fd);
205    return gFalse;
206  }
207
208  l = (struct _ft_face_data *) gmallocn (1, sizeof (struct _ft_face_data));
209  *l = tmpl;
210  l->prev = NULL;
211  l->next = _ft_open_faces;
212  if (_ft_open_faces)
213    _ft_open_faces->prev = l;
214  _ft_open_faces = l;
215
216  l->font_face = cairo_ft_font_face_create_for_ft_face (tmpl.face,
217                                                          FT_LOAD_NO_HINTING |
218                                                          FT_LOAD_NO_BITMAP);
219  if (cairo_font_face_set_user_data (l->font_face,
220                                     &_ft_cairo_key,
221                                     l,
222                                     _ft_done_face))
223  {
224    cairo_font_face_destroy (l->font_face);
225    _ft_done_face (l);
226    return NULL;
227  }
228
229  *face_out = l->face;
230  *font_face_out = l->font_face;
231  return gTrue;
232}
233#else
234#define _ft_new_face _ft_new_face_uncached
235#endif
236
237CairoFont *CairoFont::create(GfxFont *gfxFont, XRef *xref, FT_Library lib, GBool useCIDs) {
238  Ref embRef;
239  Object refObj, strObj;
240  GooString *tmpFileName, *fileName,*tmpFileName2;
241  DisplayFontParam *dfp;
242  FILE *tmpFile;
243  int c, i, n;
244  GfxFontType fontType;
245  char **enc;
246  char *name;
247  FoFiTrueType *ff;
248  FoFiType1C *ff1c;
249  Ref ref;
250  FT_Face face;
251  cairo_font_face_t *font_face;
252
253  Gushort *codeToGID;
254  int codeToGIDLen;
255 
256  dfp = NULL;
257  codeToGID = NULL;
258  codeToGIDLen = 0;
259
260  GBool substitute = gFalse;
261 
262  ref = *gfxFont->getID();
263  fontType = gfxFont->getType();
264
265  tmpFileName = NULL;
266
267  if (gfxFont->getEmbeddedFontID(&embRef)) {
268    if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
269      error(-1, "Couldn't create temporary font file");
270      goto err2;
271    }
272   
273    refObj.initRef(embRef.num, embRef.gen);
274    refObj.fetch(xref, &strObj);
275    refObj.free();
276    if (!strObj.isStream()) {
277      error(-1, "Embedded font object is wrong type");
278      strObj.free();
279      fclose(tmpFile);
280      goto err2;
281    }
282    strObj.streamReset();
283    while ((c = strObj.streamGetChar()) != EOF) {
284      fputc(c, tmpFile);
285    }
286    strObj.streamClose();
287    strObj.free();
288    fclose(tmpFile);
289    fileName = tmpFileName;
290   
291  } else if (!(fileName = gfxFont->getExtFontFile())) {
292    // look for a display font mapping or a substitute font
293    dfp = NULL;
294    if (gfxFont->getName()) {
295      dfp = globalParams->getDisplayFont(gfxFont);
296    }
297    if (!dfp) {
298      error(-1, "Couldn't find a font for '%s'",
299            gfxFont->getName() ? gfxFont->getName()->getCString()
300            : "(unnamed)");
301      goto err2;
302    }
303    switch (dfp->kind) {
304    case displayFontT1:
305      fileName = dfp->t1.fileName;
306      fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
307      break;
308    case displayFontTT:
309      fileName = dfp->tt.fileName;
310      fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
311      break;
312    }
313    substitute = gTrue;
314  }
315
316  switch (fontType) {
317  case fontType1:
318  case fontType1C:
319    if (! _ft_new_face (lib, fileName->getCString(), &face, &font_face)) {
320      error(-1, "could not create type1 face");
321      goto err2;
322    }
323   
324    enc = ((Gfx8BitFont *)gfxFont)->getEncoding();
325   
326    codeToGID = (Gushort *)gmallocn(256, sizeof(int));
327    codeToGIDLen = 256;
328    for (i = 0; i < 256; ++i) {
329      codeToGID[i] = 0;
330      if ((name = enc[i])) {
331        codeToGID[i] = (Gushort)FT_Get_Name_Index(face, name);
332      }
333    }
334    break;
335   
336  case fontCIDType2:
337    codeToGID = NULL;
338    n = 0;
339    if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
340      n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
341      if (n) {
342        codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
343        memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
344                n * sizeof(Gushort));
345      }
346    } else {
347      ff = FoFiTrueType::load(fileName->getCString());
348      if (! ff)
349        goto err2;
350      codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
351      delete ff;
352    }
353    codeToGIDLen = n;
354    /* Fall through */
355  case fontTrueType:
356    if (!(ff = FoFiTrueType::load(fileName->getCString()))) {
357      error(-1, "failed to load truetype font\n");
358      goto err2;
359    }
360    /* This might be set already for the CIDType2 case */
361    if (fontType == fontTrueType) {
362      codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
363      codeToGIDLen = 256;
364    }
365    if (!openTempFile(&tmpFileName2, &tmpFile, "wb", NULL)) {
366      delete ff;
367      error(-1, "failed to open truetype tempfile\n");
368      goto err2;
369    }
370    ff->writeTTF(&fileWrite, tmpFile);
371    fclose(tmpFile);
372    delete ff;
373
374    if (! _ft_new_face (lib, tmpFileName2->getCString(), &face, &font_face)) {
375      error(-1, "could not create truetype face\n");
376      goto err2;
377    }
378    unlink (tmpFileName2->getCString());
379    delete tmpFileName2;
380    break;
381   
382  case fontCIDType0:
383  case fontCIDType0C:
384
385    codeToGID = NULL;
386    codeToGIDLen = 0;
387
388    if (!useCIDs)
389    {
390      if ((ff1c = FoFiType1C::load(fileName->getCString()))) {
391        codeToGID = ff1c->getCIDToGIDMap(&codeToGIDLen);
392        delete ff1c;
393      }
394    }
395
396    if (! _ft_new_face (lib, fileName->getCString(), &face, &font_face)) {
397      gfree(codeToGID);
398      codeToGID = NULL;
399      error(-1, "could not create cid face\n");
400      goto err2;
401    }
402    break;
403   
404  default:
405    printf ("font type not handled\n");
406    goto err2;
407    break;
408  }
409
410  // delete the (temporary) font file -- with Unix hard link
411  // semantics, this will remove the last link; otherwise it will
412  // return an error, leaving the file to be deleted later
413  if (fileName == tmpFileName) {
414    unlink (fileName->getCString());
415    delete tmpFileName;
416  }
417
418  return new CairoFont(ref,
419                       font_face, face,
420                       codeToGID, codeToGIDLen,
421                       substitute);
422
423 err2:
424  /* hmm? */
425  printf ("some font thing failed\n");
426  return NULL;
427}
428
429CairoFont::CairoFont(Ref ref, cairo_font_face_t *cairo_font_face, FT_Face face,
430    Gushort *codeToGID, int codeToGIDLen, GBool substitute) : ref(ref), cairo_font_face(cairo_font_face),
431                                            face(face), codeToGID(codeToGID),
432                                            codeToGIDLen(codeToGIDLen), substitute(substitute) { }
433
434CairoFont::~CairoFont() {
435  cairo_font_face_destroy (cairo_font_face);
436  gfree(codeToGID);
437}
438
439GBool
440CairoFont::matches(Ref &other) {
441  return (other.num == ref.num && other.gen == ref.gen);
442}
443
444cairo_font_face_t *
445CairoFont::getFontFace(void) {
446  return cairo_font_face;
447}
448
449unsigned long
450CairoFont::getGlyph(CharCode code,
451                    Unicode *u, int uLen) {
452  FT_UInt gid;
453
454  if (codeToGID && code < codeToGIDLen) {
455    gid = (FT_UInt)codeToGID[code];
456  } else {
457    gid = (FT_UInt)code;
458  }
459  return gid;
460}
461
462double
463CairoFont::getSubstitutionCorrection(GfxFont *gfxFont)
464{
465  double w1, w2,w3;
466  CharCode code;
467  char *name;
468
469  // for substituted fonts: adjust the font matrix -- compare the
470  // width of 'm' in the original font and the substituted font
471  if (isSubstitute() && !gfxFont->isCIDFont()) {
472    for (code = 0; code < 256; ++code) {
473      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
474          name[0] == 'm' && name[1] == '\0') {
475        break;
476      }
477    }
478    if (code < 256) {
479      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
480      {
481        cairo_matrix_t m;
482        cairo_matrix_init_identity(&m);
483        cairo_font_options_t *options = cairo_font_options_create();
484        cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
485        cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF);
486        cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(cairo_font_face, &m, &m, options);
487
488        cairo_text_extents_t extents;
489        cairo_scaled_font_text_extents(scaled_font, "m", &extents);
490
491        cairo_scaled_font_destroy(scaled_font);
492        cairo_font_options_destroy(options);
493        w3 = extents.width;
494        w2 = extents.x_advance;
495      }
496      if (!gfxFont->isSymbolic()) {
497        // if real font is substantially narrower than substituted
498        // font, reduce the font size accordingly
499        if (w1 > 0.01 && w1 < 0.9 * w2) {
500          w1 /= w2;
501          return w1;
502        }
503      }
504    }
505  }
506  return 1.0;
507}
508
509//------------------------------------------------------------------------
510// CairoFontEngine
511//------------------------------------------------------------------------
512
513CairoFontEngine::CairoFontEngine(FT_Library libA) {
514  int i;
515
516  lib = libA;
517  for (i = 0; i < cairoFontCacheSize; ++i) {
518    fontCache[i] = NULL;
519  }
520 
521  FT_Int major, minor, patch;
522  // as of FT 2.1.8, CID fonts are indexed by CID instead of GID
523  FT_Library_Version(lib, &major, &minor, &patch);
524  useCIDs = major > 2 ||
525            (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
526}
527
528CairoFontEngine::~CairoFontEngine() {
529  int i;
530 
531  for (i = 0; i < cairoFontCacheSize; ++i) {
532    if (fontCache[i])
533      delete fontCache[i];
534  }
535}
536
537CairoFont *
538CairoFontEngine::getFont(GfxFont *gfxFont, XRef *xref) {
539  int i, j;
540  Ref ref;
541  CairoFont *font;
542  GfxFontType fontType;
543 
544  fontType = gfxFont->getType();
545  if (fontType == fontType3) {
546    /* Need to figure this out later */
547    //    return NULL;
548  }
549
550  ref = *gfxFont->getID();
551
552  for (i = 0; i < cairoFontCacheSize; ++i) {
553    font = fontCache[i];
554    if (font && font->matches(ref)) {
555      for (j = i; j > 0; --j) {
556        fontCache[j] = fontCache[j-1];
557      }
558      fontCache[0] = font;
559      return font;
560    }
561  }
562 
563  font = CairoFont::create (gfxFont, xref, lib, useCIDs);
564  //XXX: if font is null should we still insert it into the cache?
565  if (fontCache[cairoFontCacheSize - 1]) {
566    delete fontCache[cairoFontCacheSize - 1];
567  }
568  for (j = cairoFontCacheSize - 1; j > 0; --j) {
569    fontCache[j] = fontCache[j-1];
570  }
571  fontCache[0] = font;
572  return font;
573}
574
Note: See TracBrowser for help on using the repository browser.