Code

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