Code

crash when we try to rasterise with a font that pango can't cope with
[inkscape.git] / src / libnrtype / FontFactory.cpp
1 /*
2  *  FontFactory.cpp
3  *  testICU
4  *
5  *   Authors:
6  *     fred
7  *     bulia byak <buliabyak@users.sf.net>
8  *
9  */
11 #include "FontFactory.h"
12 #include <libnrtype/font-instance.h>
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include <glib/gmem.h>
20 #include <glibmm/i18n.h> // _()
22 /* Freetype2 */
23 # include <pango/pangoft2.h>
26 // need to avoid using the size field
27 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
28     int h = 0;
29     h *= 1128467;
30     char const *theF = pango_font_description_get_family(x);
31     h += (theF)?g_str_hash(theF):0;
32     h *= 1128467;
33     h += (int)pango_font_description_get_style(x);
34     h *= 1128467;
35     h += (int)pango_font_description_get_variant(x);
36     h *= 1128467;
37     h += (int)pango_font_description_get_weight(x);
38     h *= 1128467;
39     h += (int)pango_font_description_get_stretch(x);
40     return h;
41 }
42 bool  font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) {
43     //if ( pango_font_description_equal(a,b) ) return true;
44     char const *fa = pango_font_description_get_family(a);
45     char const *fb = pango_font_description_get_family(b);
46     if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
47     if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
48     if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
49     if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
50     if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
51     if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
52     return true;
53 }
55 /////////////////// helper functions
57 /**
58  * A wrapper for strcasestr that also provides an implementation for Win32.
59  */
60 static bool
61 ink_strstr(char const *haystack, char const *pneedle)
62 {
63     // windows has no strcasestr implementation, so here is ours...
64     // stolen from nmap
65     /* FIXME: This is broken for e.g. ink_strstr("aab", "ab").  Report to nmap.
66      *
67      * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
68      * of tolower.  Given that haystack is a font name (i.e. fairly short), it should be ok to
69      * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
70      *
71      * Rather than fixing in inkscape, consider getting rid of this routine, instead using
72      * strdown and plain strstr at caller.  We have control over the needle values, so we can
73      * modify the callers rather than calling strdown there.
74      */
75     char buf[512];
76     register char const *p;
77     char *needle, *q, *foundto;
78     if (!*pneedle) return true;
79     if (!haystack) return false;
80         
81     needle = buf;
82     p = pneedle; q = needle;
83     while ((*q++ = tolower(*p++)))
84         ;
85     p = haystack - 1; foundto = needle;
86     while (*++p) {
87         if (tolower(*p) == *foundto) {
88             if (!*++foundto) {
89                 /* Yeah, we found it */
90                 return true;
91             }
92         } else foundto = needle;
93     }
94     return false;
95 }
97 /**
98  * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
99  */
100 // FIXME: make this UTF8, add non-English style names
101 static bool
102 is_regular(char const *s)
104     if (ink_strstr(s, "Regular")) return true;
105     if (ink_strstr(s, "Roman")) return true;
106     if (ink_strstr(s, "Normal")) return true;
107     if (ink_strstr(s, "Plain")) return true;
108     return false;
111 /**
112  * Non-bold fonts are 'Medium' or 'Book'
113  */
114 static bool
115 is_nonbold(char const *s)
117     if (ink_strstr(s, "Medium")) return true;
118     if (ink_strstr(s, "Book")) return true;
119     return false;
122 /**
123  * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
124  */
125 static bool
126 is_italic(char const *s)
128     if (ink_strstr(s, "Italic")) return true;
129     if (ink_strstr(s, "Oblique")) return true;
130     if (ink_strstr(s, "Slanted")) return true;
131     return false;
134 /**
135  * Bold fonts are 'Bold'
136  */
137 static bool
138 is_bold(char const *s)
140     if (ink_strstr(s, "Bold")) return true;
141     return false;
144 /**
145  * Caps fonts are 'Caps'
146  */
147 static bool
148 is_caps(char const *s)
150     if (ink_strstr(s, "Caps")) return true;
151     return false;
154 #if 0 /* FIXME: These are all unused.  Please delete them or use them (presumably in
155 * style_name_compare). */
156 /**
157  * Monospaced fonts are 'Mono'
158  */
159 static bool
160 is_mono(char const *s)
162     if (ink_strstr(s, "Mono")) return true;
163     return false;
166 /**
167  * Rounded fonts are 'Round'
168  */
169 static bool
170 is_round(char const *s)
172     if (ink_strstr(s, "Round")) return true;
173     return false;
176 /**
177  * Outline fonts are 'Outline'
178  */
179 static bool
180 is_outline(char const *s)
182     if (ink_strstr(s, "Outline")) return true;
183     return false;
186 /**
187  * Swash fonts are 'Swash'
188  */
189 static bool
190 is_swash(char const *s)
192     if (ink_strstr(s, "Swash")) return true;
193     return false;
195 #endif
197 /**
198  * Determines if two style names match.  This allows us to match
199  * based on the type of style rather than simply doing string matching,
200  * because for instance 'Plain' and 'Normal' mean the same thing.
201  * 
202  * Q:  Shouldn't this include the other tests such as is_outline, etc.?
203  * Q:  Is there a problem with strcasecmp on Win32?  Should it use stricmp?
204  */
205 static int
206 style_name_compare(void const *aa, void const *bb)
208     char const *a = (char const *) aa;
209     char const *b = (char const *) bb;
210         
211     if (is_regular(a) && !is_regular(b)) return -1;
212     if (is_regular(b) && !is_regular(a)) return 1;
213         
214     if (is_bold(a) && !is_bold(b)) return 1;
215     if (is_bold(b) && !is_bold(a)) return -1;
216         
217     if (is_italic(a) && !is_italic(b)) return 1;
218     if (is_italic(b) && !is_italic(a)) return -1;
219         
220     if (is_nonbold(a) && !is_nonbold(b)) return 1;
221     if (is_nonbold(b) && !is_nonbold(a)) return -1;
222         
223     if (is_caps(a) && !is_caps(b)) return 1;
224     if (is_caps(b) && !is_caps(a)) return -1;
225         
226     return strcasecmp(a, b);
229 static int
230 style_record_compare(void const *aa, void const *bb)
232     NRStyleRecord const *a = (NRStyleRecord const *) aa;
233     NRStyleRecord const *b = (NRStyleRecord const *) bb;
234         
235     return (style_name_compare(a->name, b->name));
238 static void font_factory_name_list_destructor(NRNameList *list) 
240     for (unsigned int i = 0; i < list->length; i++) 
241         g_free(list->names[i]);
242     if ( list->names ) g_free(list->names);
245 static void font_factory_style_list_destructor(NRStyleList *list) 
247     for (unsigned int i = 0; i < list->length; i++) {
248         g_free((void *) (list->records)[i].name);
249         g_free((void *) (list->records)[i].descr);
250     }
251     if ( list->records ) g_free(list->records);
254 /**
255  * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
256  */
257 static int
258 family_name_compare(void const *a, void const *b)
260 #ifndef WIN32
261     return strcasecmp((*((char const **) a)), (*((char const **) b)));
262 #else
263     return stricmp((*((char const **) a)), (*((char const **) b)));
264 #endif
267 void noop(...) {}
268 //#define PANGO_DEBUG g_print
269 #define PANGO_DEBUG noop
273 ///////////////////// FontFactory
274 #ifndef USE_PANGO_WIN32
275 // the substitute function to tell fontconfig to enforce outline fonts
276 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
278     FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
279     //char *fam = NULL;
280     //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
281     //printf("subst_f on %s\n",fam);
283 #endif
286 font_factory *font_factory::lUsine = NULL;
288 font_factory *font_factory::Default(void)
290     if ( lUsine == NULL ) lUsine = new font_factory;
291     return lUsine;
294 font_factory::font_factory(void)
296     fontSize = 512;
297     nbEnt = 0;
298     maxEnt = 32;
299     ents = (font_entry*)g_malloc(maxEnt*sizeof(font_entry));
301 #ifdef USE_PANGO_WIN32
302     hScreenDC = pango_win32_get_dc();
303     fontServer = pango_win32_font_map_for_display();
304     fontContext = pango_win32_get_context();
305     pangoFontCache = pango_win32_font_map_get_font_cache(fontServer);
306 #else
307     fontServer = pango_ft2_font_map_new();
308     pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
309     fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
310     pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
311 #endif
314 font_factory::~font_factory(void)
316     for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
317     if ( ents ) g_free(ents);
319     g_object_unref(fontServer);
320 #ifdef USE_PANGO_WIN32
321     pango_win32_shutdown_display();
322 #else
323     //pango_ft2_shutdown_display();
324 #endif
325     //g_object_unref(fontContext);
328 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
330     PangoFontDescription *temp_descr = pango_font_description_from_string(style);
331     pango_font_description_set_family(temp_descr,family);
332     font_instance *res = Face(temp_descr);
333     pango_font_description_free(temp_descr);
334     return res;
337 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
339 #ifdef USE_PANGO_WIN32
340     // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
341     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
342 #else
343     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
344 #endif
345         
346     font_instance *res = NULL;
347         
348     if ( loadedFaces.find(descr) == loadedFaces.end() ) {
349         // not yet loaded
350         PangoFont *nFace = NULL;
352         // workaround for bug #1025565.
353         // fonts without families blow up Pango.
354         if (pango_font_description_get_family(descr) != NULL) {
355             nFace = pango_font_map_load_font(fontServer,fontContext,descr);
356         }
357         else {
358             g_warning(_("Ignoring font without family that will crash Pango"));
359         }
361         if ( nFace ) {
362             // duplicate FcPattern, the hard way
363             res = new font_instance();
364             // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at
365             // in the hash_map.  the descr of the returned pangofont may differ from what was asked, so we don't know (at this 
366             // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems)
367             res->descr = pango_font_description_copy(descr);
368             res->daddy = this;
369             res->InstallFace(nFace);
370             if ( res->pFont == NULL ) {
371                 // failed to install face -> bitmap font
372                 // printf("face failed\n");
373                 res->daddy = NULL;
374                 delete res;
375                 res = NULL;
376                 if ( canFail ) {
377                     char *tc = pango_font_description_to_string(descr);
378                     PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
379                     g_free(tc);
380                     pango_font_description_set_family(descr,"Sans");
381                     res = Face(descr,false);
382                 }
383             } else {
384                 loadedFaces[res->descr]=res;
385                 res->Ref();
386                 AddInCache(res);
387             }
388         } else {
389             // no match
390             if ( canFail ) {
391                 PANGO_DEBUG("falling back to Sans\n");
392                 descr = pango_font_description_new();
393                 pango_font_description_set_family(descr,"Sans");
394                 res = Face(descr,false);
395                 pango_font_description_free(descr);
396             }
397         }
398     } else {
399         // already here
400         res = loadedFaces[descr];
401         res->Ref();
402         AddInCache(res);
403     }
404     res->InitTheFace();
405     return res;
408 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
410     PangoFontDescription *temp_descr = pango_font_description_new();
411     pango_font_description_set_family(temp_descr,family);
412     pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
413     pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
414     pango_font_description_set_style(temp_descr,(PangoStyle)style);
415     pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
416     font_instance *res = Face(temp_descr);
417     pango_font_description_free(temp_descr);
418     return res;
421 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
423     PangoFontDescription *temp_descr = pango_font_description_new();
424         
425     pango_font_description_set_family(temp_descr, family);
426         
427     if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
428         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
429     } else {
430         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
431     }
432         
433     if ( apos.italic ) {
434         pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
435     } else if ( apos.oblique ) {
436         pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
437     } else {
438         pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
439     }
440         
441     if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
442         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
443     } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
444         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
445     } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
446         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
447     } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
448         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
449     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
450         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
451     } else {
452         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
453     }
454         
455     if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
456         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
457     } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
458         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
459     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
460         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
461     } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
462         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
463     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
464         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
465     } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
466         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
467     } else {
468         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
469     }
470         
471     font_instance *res = Face(temp_descr);
472     pango_font_description_free(temp_descr);
473     return res;
476 void font_factory::UnrefFace(font_instance *who)
478     if ( who == NULL ) return;
479     if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
480         // not found
481         char *tc = pango_font_description_to_string(who->descr);
482         g_warning("unrefFace %p=%s: failed\n",who,tc);
483         g_free(tc);
484     } else {
485         loadedFaces.erase(loadedFaces.find(who->descr));
486         //                      printf("unrefFace %p: success\n",who);
487     }
490 NRNameList *font_factory::Families(NRNameList *flist)
492     PangoFontFamily**  fams = NULL;
493     int                nbFam = 0;
494     pango_font_map_list_families(fontServer, &fams, &nbFam);
495         
496     PANGO_DEBUG("got %d families\n", nbFam);
497         
498     flist->length = nbFam;
499     flist->names = (guchar **)g_malloc(nbFam*sizeof(guchar*));
500     flist->destructor = font_factory_name_list_destructor;
501         
502     for (int i = 0;i < nbFam;i++) {
503 // Note: on Windows, pango_font_family_get_name always returns lowercase name.
504 // As a result the list of fonts in the dialog is lowercase.
505 // We could work around by loading the font and taking pango_font_description_get_family from its descr (that gives correct case),
506 // but this is slow, and it's better to fix Pango instead.
507         flist->names[i]=(guchar*)strdup(pango_font_family_get_name(fams[i]));
508     }
509         
510     qsort(flist->names, nbFam, sizeof(guchar *), family_name_compare);
511         
512     g_free(fams);
513         
514     return flist;
517 NRStyleList *font_factory::Styles(gchar const *family, NRStyleList *slist)
519     PangoFontFamily *theFam = NULL;
520         
521     // search available families
522     {
523         PangoFontFamily**  fams = NULL;
524         int                nbFam = 0;
525         pango_font_map_list_families(fontServer, &fams, &nbFam);
526                 
527         for (int i = 0;i < nbFam;i++) {
528             char const *fname = pango_font_family_get_name(fams[i]);
529             if ( fname && strcmp(family,fname) == 0 ) {
530                 theFam = fams[i];
531                 break;
532             }
533         }
534                 
535         g_free(fams);
536     }
537         
538     // nothing found
539     if ( theFam == NULL ) {
540         slist->length = 0;
541         slist->records = NULL;
542         slist->destructor = NULL;
543         return slist;
544     }
545         
546     // search faces in the found family
547     PangoFontFace**  faces = NULL;
548     int nFaces = 0;
549     pango_font_family_list_faces(theFam, &faces, &nFaces);
550         
551     slist->records = (NRStyleRecord *) g_malloc(nFaces * sizeof(NRStyleRecord));
552     slist->destructor = font_factory_style_list_destructor;
553         
554     int nr = 0;
555     for (int i = 0; i < nFaces; i++) {
556                 
557         // no unnamed faces
558         if (pango_font_face_get_face_name(faces[i]) == NULL)
559             continue;
560         PangoFontDescription *nd = pango_font_face_describe(faces[i]);
561         if (nd == NULL)
562             continue;
563         char const *descr = pango_font_description_to_string(nd);
564         if (descr == NULL) {
565             pango_font_description_free(nd);
566             continue;
567         }
568                 
569         char const *name = g_strdup(pango_font_face_get_face_name(faces[i]));
570         pango_font_description_free(nd);
571                 
572         slist->records[nr].name = name;
573         slist->records[nr].descr = descr;
574         nr ++;
575     }
576         
577     slist->length = nr;
578         
579     qsort(slist->records, slist->length, sizeof(NRStyleRecord), style_record_compare);
580     /* effic: Consider doing strdown and all the is_italic etc. tests once off and store the
581      * results in a table, rather than having the sort invoke multiple is_italic tests per
582      * record.
583      */
584         
585     g_free(faces);
586         
587     return slist;
590 void font_factory::AddInCache(font_instance *who)
592     if ( who == NULL ) return;
593     for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
594     for (int i = 0;i < nbEnt;i++) {
595         if ( ents[i].f == who ) {
596             //                  printf("present\n");
597             ents[i].age += 1.0;
598             return;
599         }
600     }
601     if ( nbEnt > maxEnt ) {
602         printf("cache sur-plein?\n");
603         return;
604     }
605     who->Ref();
606     if ( nbEnt == maxEnt ) {
607         int    bi = 0;
608         double ba = ents[bi].age;
609         for (int i = 1;i < nbEnt;i++) {
610             if ( ents[i].age < ba ) {
611                 bi = i;
612                 ba = ents[bi].age;
613             }
614         }
615         ents[bi].f->Unref();
616         ents[bi]=ents[--nbEnt];
617     }
618     ents[nbEnt].f = who;
619     ents[nbEnt].age = 1.0;
620     nbEnt++;
624 /*
625   Local Variables:
626   mode:c++
627   c-file-style:"stroustrup"
628   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
629   indent-tabs-mode:nil
630   fill-column:99
631   End:
632 */
633 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :