Code

Merging in from trunk
[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 #define PANGO_ENABLE_ENGINE
13 #include "FontFactory.h"
14 #include <libnrtype/font-instance.h>
16 #include <glibmm.h>
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <glib/gmem.h>
24 #include <glibmm/i18n.h> // _()
26 /* Freetype2 */
27 # include <pango/pangoft2.h>
30 // need to avoid using the size field
31 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
32     int h = 0;
33     h *= 1128467;
34     char const *theF = pango_font_description_get_family(x);
35     h += (theF)?g_str_hash(theF):0;
36     h *= 1128467;
37     h += (int)pango_font_description_get_style(x);
38     h *= 1128467;
39     h += (int)pango_font_description_get_variant(x);
40     h *= 1128467;
41     h += (int)pango_font_description_get_weight(x);
42     h *= 1128467;
43     h += (int)pango_font_description_get_stretch(x);
44     return h;
45 }
46 bool  font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) {
47     //if ( pango_font_description_equal(a,b) ) return true;
48     char const *fa = pango_font_description_get_family(a);
49     char const *fb = pango_font_description_get_family(b);
50     if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
51     if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
52     if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
53     if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
54     if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
55     if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
56     return true;
57 }
59 /////////////////// helper functions
61 /**
62  * A wrapper for strcasestr that also provides an implementation for Win32.
63  */
64 static bool
65 ink_strstr(char const *haystack, char const *pneedle)
66 {
67     // windows has no strcasestr implementation, so here is ours...
68     // stolen from nmap
69     /* FIXME: This is broken for e.g. ink_strstr("aab", "ab").  Report to nmap.
70      *
71      * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
72      * of tolower.  Given that haystack is a font name (i.e. fairly short), it should be ok to
73      * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
74      *
75      * Rather than fixing in inkscape, consider getting rid of this routine, instead using
76      * strdown and plain strstr at caller.  We have control over the needle values, so we can
77      * modify the callers rather than calling strdown there.
78      */
79     char buf[512];
80     register char const *p;
81     char *needle, *q, *foundto;
82     if (!*pneedle) return true;
83     if (!haystack) return false;
85     needle = buf;
86     p = pneedle; q = needle;
87     while ((*q++ = tolower(*p++)))
88         ;
89     p = haystack - 1; foundto = needle;
90     while (*++p) {
91         if (tolower(*p) == *foundto) {
92             if (!*++foundto) {
93                 /* Yeah, we found it */
94                 return true;
95             }
96         } else foundto = needle;
97     }
98     return false;
99 }
101 /**
102  * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
103  */
104 // FIXME: make this UTF8, add non-English style names
105 static bool
106 is_regular(char const *s)
108     if (ink_strstr(s, "Regular")) return true;
109     if (ink_strstr(s, "Roman")) return true;
110     if (ink_strstr(s, "Normal")) return true;
111     if (ink_strstr(s, "Plain")) return true;
112     return false;
115 /**
116  * Non-bold fonts are 'Medium' or 'Book'
117  */
118 static bool
119 is_nonbold(char const *s)
121     if (ink_strstr(s, "Medium")) return true;
122     if (ink_strstr(s, "Book")) return true;
123     return false;
126 /**
127  * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
128  */
129 static bool
130 is_italic(char const *s)
132     if (ink_strstr(s, "Italic")) return true;
133     if (ink_strstr(s, "Oblique")) return true;
134     if (ink_strstr(s, "Slanted")) return true;
135     return false;
138 /**
139  * Bold fonts are 'Bold'
140  */
141 static bool
142 is_bold(char const *s)
144     if (ink_strstr(s, "Bold")) return true;
145     return false;
148 /**
149  * Caps fonts are 'Caps'
150  */
151 static bool
152 is_caps(char const *s)
154     if (ink_strstr(s, "Caps")) return true;
155     return false;
158 #if 0 /* FIXME: These are all unused.  Please delete them or use them (presumably in
159 * style_name_compare). */
160 /**
161  * Monospaced fonts are 'Mono'
162  */
163 static bool
164 is_mono(char const *s)
166     if (ink_strstr(s, "Mono")) return true;
167     return false;
170 /**
171  * Rounded fonts are 'Round'
172  */
173 static bool
174 is_round(char const *s)
176     if (ink_strstr(s, "Round")) return true;
177     return false;
180 /**
181  * Outline fonts are 'Outline'
182  */
183 static bool
184 is_outline(char const *s)
186     if (ink_strstr(s, "Outline")) return true;
187     return false;
190 /**
191  * Swash fonts are 'Swash'
192  */
193 static bool
194 is_swash(char const *s)
196     if (ink_strstr(s, "Swash")) return true;
197     return false;
199 #endif
201 /**
202  * Determines if two style names match.  This allows us to match
203  * based on the type of style rather than simply doing string matching,
204  * because for instance 'Plain' and 'Normal' mean the same thing.
205  *
206  * Q:  Shouldn't this include the other tests such as is_outline, etc.?
207  * Q:  Is there a problem with strcasecmp on Win32?  Should it use stricmp?
208  */
209 int
210 style_name_compare(char const *aa, char const *bb)
212     char const *a = (char const *) aa;
213     char const *b = (char const *) bb;
215     if (is_regular(a) && !is_regular(b)) return -1;
216     if (is_regular(b) && !is_regular(a)) return 1;
218     if (is_bold(a) && !is_bold(b)) return 1;
219     if (is_bold(b) && !is_bold(a)) return -1;
221     if (is_italic(a) && !is_italic(b)) return 1;
222     if (is_italic(b) && !is_italic(a)) return -1;
224     if (is_nonbold(a) && !is_nonbold(b)) return 1;
225     if (is_nonbold(b) && !is_nonbold(a)) return -1;
227     if (is_caps(a) && !is_caps(b)) return 1;
228     if (is_caps(b) && !is_caps(a)) return -1;
230     return strcasecmp(a, b);
233 /*
234  defined but not used:
236 static int
237 style_record_compare(void const *aa, void const *bb)
239     NRStyleRecord const *a = (NRStyleRecord const *) aa;
240     NRStyleRecord const *b = (NRStyleRecord const *) bb;
242     return (style_name_compare(a->name, b->name));
245 static void font_factory_name_list_destructor(NRNameList *list)
247     for (unsigned int i = 0; i < list->length; i++)
248         g_free(list->names[i]);
249     if ( list->names ) g_free(list->names);
252 static void font_factory_style_list_destructor(NRStyleList *list)
254     for (unsigned int i = 0; i < list->length; i++) {
255         g_free((void *) (list->records)[i].name);
256         g_free((void *) (list->records)[i].descr);
257     }
258     if ( list->records ) g_free(list->records);
260 */
262 /**
263  * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
264  */
265 int
266 family_name_compare(char const *a, char const *b)
268 #ifndef WIN32
269     return strcasecmp((*((char const **) a)), (*((char const **) b)));
270 #else
271     return stricmp((*((char const **) a)), (*((char const **) b)));
272 #endif
275 void noop(...) {}
276 //#define PANGO_DEBUG g_print
277 #define PANGO_DEBUG noop
281 ///////////////////// FontFactory
282 #ifndef USE_PANGO_WIN32
283 // the substitute function to tell fontconfig to enforce outline fonts
284 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
286     FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
287     //char *fam = NULL;
288     //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
289     //printf("subst_f on %s\n",fam);
291 #endif
294 font_factory *font_factory::lUsine = NULL;
296 font_factory *font_factory::Default(void)
298     if ( lUsine == NULL ) lUsine = new font_factory;
299     return lUsine;
302 font_factory::font_factory(void)
304     fontSize = 512;
305     nbEnt = 0;
306     maxEnt = 32;
307     ents = (font_entry*)g_malloc(maxEnt*sizeof(font_entry));
309 #ifdef USE_PANGO_WIN32
310     hScreenDC = pango_win32_get_dc();
311     fontServer = pango_win32_font_map_for_display();
312     fontContext = pango_win32_get_context();
313     pangoFontCache = pango_win32_font_map_get_font_cache(fontServer);
314 #else
315     fontServer = pango_ft2_font_map_new();
316     pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
317     fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
318     pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
319 #endif
322 font_factory::~font_factory(void)
324     for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
325     if ( ents ) g_free(ents);
327     g_object_unref(fontServer);
328 #ifdef USE_PANGO_WIN32
329     pango_win32_shutdown_display();
330 #else
331     //pango_ft2_shutdown_display();
332 #endif
333     //g_object_unref(fontContext);
335     // Delete the pango font pointers in the string to instance map
336     PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
337     while (it != fontInstanceMap.end()) {
338         pango_font_description_free((*it).second);
339         it++;
340     }
344 Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font)
346     Glib::ustring pangoString;
348     g_assert(font);
350     if (font) {
351         // Once the format for the font specification is decided, it must be
352         // kept.. if it is absolutely necessary to change it, the attribute
353         // it is written to needs to have a new version so the legacy files
354         // can be read.
356         PangoFontDescription *copy = pango_font_description_copy(font);
358         pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE);
359         pangoString = Glib::ustring(pango_font_description_to_string(copy));
361         pango_font_description_free(copy);
363     }
365     return pangoString;
368 Glib::ustring font_factory::ConstructFontSpecification(font_instance *font)
370     Glib::ustring pangoString;
372     g_assert(font);
374     if (font) {
375         pangoString = ConstructFontSpecification(font->descr);
376     }
378     return pangoString;
381 Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr)
383     Glib::ustring family;
385     g_assert(fontDescr);
387     if (fontDescr) {
388         // For now, keep it as family name taken from pango
389         family = pango_font_description_get_family(fontDescr);
390     }
392     return family;
395 Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr)
397     Glib::ustring style;
399     g_assert(fontDescr);
401     if (fontDescr) {
402         PangoFontDescription *fontDescrCopy = pango_font_description_copy(fontDescr);
404         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_FAMILY);
405         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_SIZE);
407         // For now, keep it as style name taken from pango
408         style = pango_font_description_to_string(fontDescrCopy);
410         pango_font_description_free(fontDescrCopy);
411     }
413     return style;
416 Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily)
418     Glib::ustring newFontSpec;
420     // Although we are using the string from pango_font_description_to_string for the
421     // font specification, we definitely cannot just set the new family in the
422     // PangoFontDescription structure and ask for a new string.  This is because
423     // what constitutes a "family" in our own UI may be different from how Pango
424     // sees it.
426     // Find the PangoFontDescription associated to this fontSpec
427     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
429     if (it != fontInstanceMap.end()) {
430         PangoFontDescription *descr = pango_font_description_copy((*it).second);
432         // Grab the UI Family string from the descr
433         Glib::ustring uiFamily = GetUIFamilyString(descr);
435         // Replace the UI Family name with the new family name
436         std::size_t found = fontSpec.find(uiFamily);
437         if (found != Glib::ustring::npos) {
438             newFontSpec = fontSpec;
439             newFontSpec.erase(found, uiFamily.size());
440             newFontSpec.insert(found, newFamily);
442             // If the new font specification does not exist in the reference maps,
443             // search for the next best match for the faces in that style
444             it = fontInstanceMap.find(newFontSpec);
445             if (it == fontInstanceMap.end()) {
447                 PangoFontDescription *newFontDescr = pango_font_description_from_string(newFontSpec.c_str());
449                 PangoFontDescription *bestMatchForNewDescr = NULL;
450                 Glib::ustring bestMatchFontDescription;
452                 bool setFirstFamilyMatch = false;
453                 for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
455                     Glib::ustring currentFontSpec = (*it).first;
457                     // Save some time by only looking at the right family
458                     if (currentFontSpec.find(newFamily) != Glib::ustring::npos) {
459                         if (!setFirstFamilyMatch) {
460                                 // This ensures that the closest match is at least within the correct
461                                 // family rather than the first font in the list
462                             bestMatchForNewDescr = pango_font_description_copy((*it).second);
463                             bestMatchFontDescription = currentFontSpec;
464                             setFirstFamilyMatch = true;
465                         } else {
466                             // Get the font description that corresponds, and
467                             // then see if we've found a better match
468                             PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second);
470                             if (pango_font_description_better_match(
471                                     newFontDescr, bestMatchForNewDescr, possibleMatch)) {
473                                 pango_font_description_free(bestMatchForNewDescr);
474                                 bestMatchForNewDescr = possibleMatch;
475                                 bestMatchFontDescription = currentFontSpec;
476                             } else {
477                                 pango_font_description_free(possibleMatch);
478                             }
479                         }
480                     }
481                 }
483                 newFontSpec = bestMatchFontDescription;
485                 pango_font_description_free(newFontDescr);
486                 pango_font_description_free(bestMatchForNewDescr);
487             }
488         }
490         pango_font_description_free(descr);
491     }
493     return newFontSpec;
496 /**
497     apply style property to the given font
498     @param fontSpec the given font
499     @param turnOn true to set italic style
500     @return the changed fontspec, if the property can not be set return an empty string
501 */
502 Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn)
504     Glib::ustring newFontSpec;
506     // Find the PangoFontDesecription that goes with this font specification string
507     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
509     if (it != fontInstanceMap.end()) {
510         // If we did find one, make a copy and set/unset the italic as needed
511         PangoFontDescription *descr = pango_font_description_copy((*it).second);
513         PangoStyle style;
514         if (turnOn) {
515             style = PANGO_STYLE_ITALIC;
516         } else {
517             style = PANGO_STYLE_NORMAL;
518         }
519         pango_font_description_set_style(descr, style);
521         newFontSpec = ConstructFontSpecification(descr);
522         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
523             if(turnOn) {
524                 // there is no PANGO_STYLE_ITALIC let's test for PANGO_STYLE_OBLIQUE
525                 style = PANGO_STYLE_OBLIQUE;
526                 pango_font_description_set_style(descr, style);
528                 newFontSpec = ConstructFontSpecification(descr);
529                 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
530                     // If the new font does not have even an oblique face, don't
531                     // allow italics to be set!
532                     newFontSpec = Glib::ustring("");
533                 }
535             } else {
536                 // If the new font does not have an italic face, don't
537                 // allow italics to be set!
538                 newFontSpec = Glib::ustring("");
539             }
540         }
542         pango_font_description_free(descr);
543     }
545     return newFontSpec;
548 /**
549     apply width property to the given font
550     @param fontSpec the given font
551     @param turnOn true to set bold
552     @return the changed fontspec, if the property can not be set return an empty string
553 */
554 Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn)
556     Glib::ustring newFontSpec;
558     // Find the PangoFontDesecription that goes with this font specification string
559     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
561     if (it != fontInstanceMap.end()) {
562         // If we did find one, make a copy and set/unset the bold as needed
563         PangoFontDescription *descr = pango_font_description_copy((*it).second);
565         PangoWeight weight;
566         if (turnOn) {
567             weight = PANGO_WEIGHT_BOLD;
568         } else {
569             weight = PANGO_WEIGHT_NORMAL;
570         }
571         pango_font_description_set_weight(descr, weight);
573         newFontSpec = ConstructFontSpecification(descr);
574         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
575             // If the new font does not have a bold face, don't
576             // allow bold to be set!
577             newFontSpec = Glib::ustring("");
578         }
580         pango_font_description_free(descr);
581     }
583     return newFontSpec;
586 /////
588 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
590     return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
593 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
595     g_assert(map);
597     if (map) {
599         // Gather the family names as listed by Pango
600         PangoFontFamily**  families = NULL;
601         int numFamilies = 0;
602         pango_font_map_list_families(fontServer, &families, &numFamilies);
604         for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) {
606             // Gather the styles for this family
607             PangoFontFace** faces = NULL;
608             int numFaces = 0;
609             pango_font_family_list_faces(families[currentFamily], &faces, &numFaces);
611             for (int currentFace=0; currentFace < numFaces; currentFace++) {
613                 // If the face has a name, describe it, and then use the
614                 // description to get the UI family and face strings
616                 if (pango_font_face_get_face_name(faces[currentFace]) == NULL) {
617                     continue;
618                 }
620                 PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
621                 if (faceDescr) {
622                     Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
623                     Glib::ustring styleUIName = GetUIStyleString(faceDescr);
625                     if (!familyUIName.empty() && !styleUIName.empty()) {
626                         // Find the right place to put the style information, adding
627                         // a map entry for the family name if it doesn't yet exist
629                         FamilyToStylesMap::iterator iter = map->find(familyUIName);
631                         if (iter == map->end()) {
632                             map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>()));
633                         }
635                         // Insert into the style list and save the info in the reference maps
636                         // only if the style does not yet exist
638                         bool exists = false;
639                         std::list<Glib::ustring> &styleList = (*map)[familyUIName];
641                         for (std::list<Glib::ustring>::iterator it=styleList.begin();
642                                  it != styleList.end();
643                                  it++) {
644                             if (*it == styleUIName) {
645                                 exists = true;
646                                 break;
647                             }
648                         }
650                         if (!exists) {
651                             styleList.push_back(styleUIName);
653                             // Add the string info needed in the reference maps
654                             fontStringMap.insert(
655                                     std::make_pair(
656                                             Glib::ustring(familyUIName) + Glib::ustring(styleUIName),
657                                             ConstructFontSpecification(faceDescr)));
658                             fontInstanceMap.insert(
659                                     std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
660                         } else {
661                             pango_font_description_free(faceDescr);
662                         }
663                     } else {
664                         pango_font_description_free(faceDescr);
665                     }
666                 }
667             }
668         }
670         // Sort the style lists
671         for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
672             (*iter).second.sort(StyleNameCompareInternal);
673         }
674     }
677 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
679     font_instance *font = NULL;
681     g_assert(style);
683     if (style) {
684         //  First try to use the font specification if it is set
685         if (style->text->font_specification.set
686             && style->text->font_specification.value
687             && *style->text->font_specification.value) {
689             font = FaceFromFontSpecification(style->text->font_specification.value);
690         }
692         // If that failed, try using the CSS information in the style
693         if (!font) {
694             font = Face(style->text->font_family.value, font_style_to_pos(*style));
695         }
696     }
698     return font;
701 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
703     PangoFontDescription *temp_descr = pango_font_description_from_string(style);
704     pango_font_description_set_family(temp_descr,family);
705     font_instance *res = Face(temp_descr);
706     pango_font_description_free(temp_descr);
707     return res;
710 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
712     font_instance *fontInstance = NULL;
714     g_assert(uiFamily && uiStyle);
715     if (uiFamily && uiStyle) {
716         Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle);
718         UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString);
720         if (uiToPangoIter != fontStringMap.end ()) {
721             PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second);
722             if (pangoToDescrIter != fontInstanceMap.end()) {
723                 // We found the pango description - now we can make a font_instance
724                 PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second);
725                 fontInstance = Face(tempDescr);
726                 pango_font_description_free(tempDescr);
727             }
728         }
729     }
731     return fontInstance;
734 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
736     font_instance *fontInstance = NULL;
738     g_assert(pangoString);
740     if (pangoString) {
741         PangoFontDescription *descr = NULL;
743         // First attempt to find the font specification in the reference map
744         PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString));
745         if (it != fontInstanceMap.end()) {
746             descr = pango_font_description_copy((*it).second);
747         }
749         // Or create a font description from the string - this may fail or
750         // produce unexpected results if the string does not have a good format
751         if (!descr) {
752             descr = pango_font_description_from_string(pangoString);
753         }
755         if (descr && (pango_font_description_get_family(descr) != NULL)) {
756             fontInstance = Face(descr);
757         }
759         if (descr) {
760             pango_font_description_free(descr);
761         }
762     }
764     return fontInstance;
767 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
769     font_instance *font = NULL;
771     g_assert(fontSpecification);
773     if (fontSpecification) {
774         // How the string is used to reconstruct a font depends on how it
775         // was constructed in ConstructFontSpecification.  As it stands,
776         // the font specification is a pango-created string
777         font = FaceFromPangoString(fontSpecification);
778     }
780     return font;
785 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
787 #ifdef USE_PANGO_WIN32
788     // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
789     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
790 #else
791     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
792 #endif
794     font_instance *res = NULL;
796     if ( loadedFaces.find(descr) == loadedFaces.end() ) {
797         // not yet loaded
798         PangoFont *nFace = NULL;
800         // workaround for bug #1025565.
801         // fonts without families blow up Pango.
802         if (pango_font_description_get_family(descr) != NULL) {
803             nFace = pango_font_map_load_font(fontServer,fontContext,descr);
804         }
805         else {
806             g_warning(_("Ignoring font without family that will crash Pango"));
807         }
809         if ( nFace ) {
810             // duplicate FcPattern, the hard way
811             res = new font_instance();
812             // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at
813             // in the hash_map.  the descr of the returned pangofont may differ from what was asked, so we don't know (at this
814             // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems)
815             res->descr = pango_font_description_copy(descr);
816             res->daddy = this;
817             res->InstallFace(nFace);
818             if ( res->pFont == NULL ) {
819                 // failed to install face -> bitmap font
820                 // printf("face failed\n");
821                 res->daddy = NULL;
822                 delete res;
823                 res = NULL;
824                 if ( canFail ) {
825                     char *tc = pango_font_description_to_string(descr);
826                     PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
827                     g_free(tc);
828                     pango_font_description_set_family(descr,"Sans");
829                     res = Face(descr,false);
830                 }
831             } else {
832                 loadedFaces[res->descr]=res;
833                 res->Ref();
834                 AddInCache(res);
835             }
836         } else {
837             // no match
838             if ( canFail ) {
839                 PANGO_DEBUG("falling back to Sans\n");
840                 descr = pango_font_description_new();
841                 pango_font_description_set_family(descr,"Sans");
842                 res = Face(descr,false);
843                 pango_font_description_free(descr);
844             }
845         }
846     } else {
847         // already here
848         res = loadedFaces[descr];
849         res->Ref();
850         AddInCache(res);
851     }
852     if(res)
853         res->InitTheFace();
854     return res;
857 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
859     PangoFontDescription *temp_descr = pango_font_description_new();
860     pango_font_description_set_family(temp_descr,family);
861     pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
862     pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
863     pango_font_description_set_style(temp_descr,(PangoStyle)style);
864     pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
865     font_instance *res = Face(temp_descr);
866     pango_font_description_free(temp_descr);
867     return res;
870 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
872     PangoFontDescription *temp_descr = pango_font_description_new();
874     pango_font_description_set_family(temp_descr, family);
876     if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
877         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
878     } else {
879         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
880     }
882     if ( apos.italic ) {
883         pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
884     } else if ( apos.oblique ) {
885         pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
886     } else {
887         pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
888     }
890     if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
891         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
892     } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
893         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
894     } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
895         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
896     } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
897         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
898     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
899         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
900     } else {
901         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
902     }
904     if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
905         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
906     } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
907         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
908     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
909         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
910     } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
911         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
912     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
913         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
914     } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
915         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
916     } else {
917         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
918     }
920     font_instance *res = Face(temp_descr);
921     pango_font_description_free(temp_descr);
922     return res;
925 void font_factory::UnrefFace(font_instance *who)
927     if ( who == NULL ) return;
928     if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
929         // not found
930         char *tc = pango_font_description_to_string(who->descr);
931         g_warning("unrefFace %p=%s: failed\n",who,tc);
932         g_free(tc);
933     } else {
934         loadedFaces.erase(loadedFaces.find(who->descr));
935         //                      printf("unrefFace %p: success\n",who);
936     }
939 void font_factory::AddInCache(font_instance *who)
941     if ( who == NULL ) return;
942     for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
943     for (int i = 0;i < nbEnt;i++) {
944         if ( ents[i].f == who ) {
945             //                  printf("present\n");
946             ents[i].age += 1.0;
947             return;
948         }
949     }
950     if ( nbEnt > maxEnt ) {
951         printf("cache sur-plein?\n");
952         return;
953     }
954     who->Ref();
955     if ( nbEnt == maxEnt ) {
956         int    bi = 0;
957         double ba = ents[bi].age;
958         for (int i = 1;i < nbEnt;i++) {
959             if ( ents[i].age < ba ) {
960                 bi = i;
961                 ba = ents[bi].age;
962             }
963         }
964         ents[bi].f->Unref();
965         ents[bi]=ents[--nbEnt];
966     }
967     ents[nbEnt].f = who;
968     ents[nbEnt].age = 1.0;
969     nbEnt++;
973 /*
974   Local Variables:
975   mode:c++
976   c-file-style:"stroustrup"
977   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
978   indent-tabs-mode:nil
979   fill-column:99
980   End:
981 */
982 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :