Code

Replace std::tr1::unordered_(map|set) with __gnu_cxx::hash_(map|set),
[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>
29 //#include <tr1/unordered_map>
30 #include <ext/hash_map>
33 typedef __gnu_cxx::hash_map<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType;
35 // need to avoid using the size field
36 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
37     int h = 0;
38     h *= 1128467;
39     char const *theF = pango_font_description_get_family(x);
40     h += (theF)?g_str_hash(theF):0;
41     h *= 1128467;
42     h += (int)pango_font_description_get_style(x);
43     h *= 1128467;
44     h += (int)pango_font_description_get_variant(x);
45     h *= 1128467;
46     h += (int)pango_font_description_get_weight(x);
47     h *= 1128467;
48     h += (int)pango_font_description_get_stretch(x);
49     return h;
50 }
51 bool  font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const {
52     //if ( pango_font_description_equal(a,b) ) return true;
53     char const *fa = pango_font_description_get_family(a);
54     char const *fb = pango_font_description_get_family(b);
55     if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
56     if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
57     if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
58     if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
59     if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
60     if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
61     return true;
62 }
64 /////////////////// helper functions
66 /**
67  * A wrapper for strcasestr that also provides an implementation for Win32.
68  */
69 static bool
70 ink_strstr(char const *haystack, char const *pneedle)
71 {
72     // windows has no strcasestr implementation, so here is ours...
73     // stolen from nmap
74     /* FIXME: This is broken for e.g. ink_strstr("aab", "ab").  Report to nmap.
75      *
76      * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
77      * of tolower.  Given that haystack is a font name (i.e. fairly short), it should be ok to
78      * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
79      *
80      * Rather than fixing in inkscape, consider getting rid of this routine, instead using
81      * strdown and plain strstr at caller.  We have control over the needle values, so we can
82      * modify the callers rather than calling strdown there.
83      */
84     char buf[512];
85     register char const *p;
86     char *needle, *q, *foundto;
87     if (!*pneedle) return true;
88     if (!haystack) return false;
90     needle = buf;
91     p = pneedle; q = needle;
92     while ((*q++ = tolower(*p++)))
93         ;
94     p = haystack - 1; foundto = needle;
95     while (*++p) {
96         if (tolower(*p) == *foundto) {
97             if (!*++foundto) {
98                 /* Yeah, we found it */
99                 return true;
100             }
101         } else foundto = needle;
102     }
103     return false;
106 /**
107  * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
108  */
109 // FIXME: make this UTF8, add non-English style names
110 static bool
111 is_regular(char const *s)
113     if (ink_strstr(s, "Regular")) return true;
114     if (ink_strstr(s, "Roman")) return true;
115     if (ink_strstr(s, "Normal")) return true;
116     if (ink_strstr(s, "Plain")) return true;
117     return false;
120 /**
121  * Non-bold fonts are 'Medium' or 'Book'
122  */
123 static bool
124 is_nonbold(char const *s)
126     if (ink_strstr(s, "Medium")) return true;
127     if (ink_strstr(s, "Book")) return true;
128     return false;
131 /**
132  * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
133  */
134 static bool
135 is_italic(char const *s)
137     if (ink_strstr(s, "Italic")) return true;
138     if (ink_strstr(s, "Oblique")) return true;
139     if (ink_strstr(s, "Slanted")) return true;
140     return false;
143 /**
144  * Bold fonts are 'Bold'
145  */
146 static bool
147 is_bold(char const *s)
149     if (ink_strstr(s, "Bold")) return true;
150     return false;
153 /**
154  * Caps fonts are 'Caps'
155  */
156 static bool
157 is_caps(char const *s)
159     if (ink_strstr(s, "Caps")) return true;
160     return false;
163 #if 0 /* FIXME: These are all unused.  Please delete them or use them (presumably in
164 * style_name_compare). */
165 /**
166  * Monospaced fonts are 'Mono'
167  */
168 static bool
169 is_mono(char const *s)
171     if (ink_strstr(s, "Mono")) return true;
172     return false;
175 /**
176  * Rounded fonts are 'Round'
177  */
178 static bool
179 is_round(char const *s)
181     if (ink_strstr(s, "Round")) return true;
182     return false;
185 /**
186  * Outline fonts are 'Outline'
187  */
188 static bool
189 is_outline(char const *s)
191     if (ink_strstr(s, "Outline")) return true;
192     return false;
195 /**
196  * Swash fonts are 'Swash'
197  */
198 static bool
199 is_swash(char const *s)
201     if (ink_strstr(s, "Swash")) return true;
202     return false;
204 #endif
206 /**
207  * Determines if two style names match.  This allows us to match
208  * based on the type of style rather than simply doing string matching,
209  * because for instance 'Plain' and 'Normal' mean the same thing.
210  *
211  * Q:  Shouldn't this include the other tests such as is_outline, etc.?
212  * Q:  Is there a problem with strcasecmp on Win32?  Should it use stricmp?
213  */
214 int
215 style_name_compare(char const *aa, char const *bb)
217     char const *a = (char const *) aa;
218     char const *b = (char const *) bb;
220     if (is_regular(a) && !is_regular(b)) return -1;
221     if (is_regular(b) && !is_regular(a)) return 1;
223     if (is_bold(a) && !is_bold(b)) return 1;
224     if (is_bold(b) && !is_bold(a)) return -1;
226     if (is_italic(a) && !is_italic(b)) return 1;
227     if (is_italic(b) && !is_italic(a)) return -1;
229     if (is_nonbold(a) && !is_nonbold(b)) return 1;
230     if (is_nonbold(b) && !is_nonbold(a)) return -1;
232     if (is_caps(a) && !is_caps(b)) return 1;
233     if (is_caps(b) && !is_caps(a)) return -1;
235     return strcasecmp(a, b);
238 /*
239  defined but not used:
241 static int
242 style_record_compare(void const *aa, void const *bb)
244     NRStyleRecord const *a = (NRStyleRecord const *) aa;
245     NRStyleRecord const *b = (NRStyleRecord const *) bb;
247     return (style_name_compare(a->name, b->name));
250 static void font_factory_name_list_destructor(NRNameList *list)
252     for (unsigned int i = 0; i < list->length; i++)
253         g_free(list->names[i]);
254     if ( list->names ) g_free(list->names);
257 static void font_factory_style_list_destructor(NRStyleList *list)
259     for (unsigned int i = 0; i < list->length; i++) {
260         g_free((void *) (list->records)[i].name);
261         g_free((void *) (list->records)[i].descr);
262     }
263     if ( list->records ) g_free(list->records);
265 */
267 /**
268  * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
269  */
270 int
271 family_name_compare(char const *a, char const *b)
273 #ifndef WIN32
274     return strcasecmp((*((char const **) a)), (*((char const **) b)));
275 #else
276     return stricmp((*((char const **) a)), (*((char const **) b)));
277 #endif
280 void noop(...) {}
281 //#define PANGO_DEBUG g_print
282 #define PANGO_DEBUG noop
286 ///////////////////// FontFactory
287 #ifndef USE_PANGO_WIN32
288 // the substitute function to tell fontconfig to enforce outline fonts
289 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
291     FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
292     //char *fam = NULL;
293     //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
294     //printf("subst_f on %s\n",fam);
296 #endif
299 font_factory *font_factory::lUsine = NULL;
301 font_factory *font_factory::Default(void)
303     if ( lUsine == NULL ) lUsine = new font_factory;
304     return lUsine;
307 font_factory::font_factory(void) :
308     nbEnt(0),
309     maxEnt(32),
310     ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))),
312 #ifdef USE_PANGO_WIN32
313     fontServer(pango_win32_font_map_for_display()),
314     fontContext(pango_win32_get_context()),
315     pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)),
316     hScreenDC(pango_win32_get_dc()),
317 #else
318     fontServer(pango_ft2_font_map_new()),
319     fontContext(0),
320 #endif
321     fontSize(512),
322     loadedPtr(new FaceMapType())
324 #ifdef USE_PANGO_WIN32
325 #else
326     pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
327     fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
328     pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
329 #endif
332 font_factory::~font_factory(void)
334     if (loadedPtr) {
335         FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr);
336         loadedPtr = 0;
337     }
339     for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
340     if ( ents ) g_free(ents);
342     g_object_unref(fontServer);
343 #ifdef USE_PANGO_WIN32
344     pango_win32_shutdown_display();
345 #else
346     //pango_ft2_shutdown_display();
347 #endif
348     //g_object_unref(fontContext);
350     // Delete the pango font pointers in the string to instance map
351     PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
352     while (it != fontInstanceMap.end()) {
353         pango_font_description_free((*it).second);
354         it++;
355     }
359 Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font)
361     Glib::ustring pangoString;
363     g_assert(font);
365     if (font) {
366         // Once the format for the font specification is decided, it must be
367         // kept.. if it is absolutely necessary to change it, the attribute
368         // it is written to needs to have a new version so the legacy files
369         // can be read.
371         PangoFontDescription *copy = pango_font_description_copy(font);
373         pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE);
374         pangoString = Glib::ustring(pango_font_description_to_string(copy));
376         pango_font_description_free(copy);
378     }
380     return pangoString;
383 Glib::ustring font_factory::ConstructFontSpecification(font_instance *font)
385     Glib::ustring pangoString;
387     g_assert(font);
389     if (font) {
390         pangoString = ConstructFontSpecification(font->descr);
391     }
393     return pangoString;
396 Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr)
398     Glib::ustring family;
400     g_assert(fontDescr);
402     if (fontDescr) {
403         // For now, keep it as family name taken from pango
404         family = pango_font_description_get_family(fontDescr);
405     }
407     return family;
410 Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr)
412     Glib::ustring style;
414     g_assert(fontDescr);
416     if (fontDescr) {
417         PangoFontDescription *fontDescrCopy = pango_font_description_copy(fontDescr);
419         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_FAMILY);
420         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_SIZE);
422         // For now, keep it as style name taken from pango
423         style = pango_font_description_to_string(fontDescrCopy);
425         pango_font_description_free(fontDescrCopy);
426     }
428     return style;
431 Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily)
433     Glib::ustring newFontSpec;
435     // Although we are using the string from pango_font_description_to_string for the
436     // font specification, we definitely cannot just set the new family in the
437     // PangoFontDescription structure and ask for a new string.  This is because
438     // what constitutes a "family" in our own UI may be different from how Pango
439     // sees it.
441     // Find the PangoFontDescription associated to this fontSpec
442     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
444     if (it != fontInstanceMap.end()) {
445         PangoFontDescription *descr = pango_font_description_copy((*it).second);
447         // Grab the UI Family string from the descr
448         Glib::ustring uiFamily = GetUIFamilyString(descr);
450         // Replace the UI Family name with the new family name
451         std::size_t found = fontSpec.find(uiFamily);
452         if (found != Glib::ustring::npos) {
453             newFontSpec = fontSpec;
454             newFontSpec.erase(found, uiFamily.size());
455             newFontSpec.insert(found, newFamily);
457             // If the new font specification does not exist in the reference maps,
458             // search for the next best match for the faces in that style
459             it = fontInstanceMap.find(newFontSpec);
460             if (it == fontInstanceMap.end()) {
462                 PangoFontDescription *newFontDescr = pango_font_description_from_string(newFontSpec.c_str());
464                 PangoFontDescription *bestMatchForNewDescr = NULL;
465                 Glib::ustring bestMatchFontDescription;
467                 bool setFirstFamilyMatch = false;
468                 for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
470                     Glib::ustring currentFontSpec = (*it).first;
472                     // Save some time by only looking at the right family
473                     if (currentFontSpec.find(newFamily) != Glib::ustring::npos) {
474                         if (!setFirstFamilyMatch) {
475                                 // This ensures that the closest match is at least within the correct
476                                 // family rather than the first font in the list
477                             bestMatchForNewDescr = pango_font_description_copy((*it).second);
478                             bestMatchFontDescription = currentFontSpec;
479                             setFirstFamilyMatch = true;
480                         } else {
481                             // Get the font description that corresponds, and
482                             // then see if we've found a better match
483                             PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second);
485                             if (pango_font_description_better_match(
486                                     newFontDescr, bestMatchForNewDescr, possibleMatch)) {
488                                 pango_font_description_free(bestMatchForNewDescr);
489                                 bestMatchForNewDescr = possibleMatch;
490                                 bestMatchFontDescription = currentFontSpec;
491                             } else {
492                                 pango_font_description_free(possibleMatch);
493                             }
494                         }
495                     }
496                 }
498                 newFontSpec = bestMatchFontDescription;
500                 pango_font_description_free(newFontDescr);
501                 pango_font_description_free(bestMatchForNewDescr);
502             }
503         }
505         pango_font_description_free(descr);
506     }
508     return newFontSpec;
511 /**
512     apply style property to the given font
513     @param fontSpec the given font
514     @param turnOn true to set italic style
515     @return the changed fontspec, if the property can not be set return an empty string
516 */
517 Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn)
519     Glib::ustring newFontSpec;
521     // Find the PangoFontDesecription that goes with this font specification string
522     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
524     if (it != fontInstanceMap.end()) {
525         // If we did find one, make a copy and set/unset the italic as needed
526         PangoFontDescription *descr = pango_font_description_copy((*it).second);
528         PangoStyle style;
529         if (turnOn) {
530             style = PANGO_STYLE_ITALIC;
531         } else {
532             style = PANGO_STYLE_NORMAL;
533         }
534         pango_font_description_set_style(descr, style);
536         newFontSpec = ConstructFontSpecification(descr);
537         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
538             if(turnOn) {
539                 // there is no PANGO_STYLE_ITALIC let's test for PANGO_STYLE_OBLIQUE
540                 style = PANGO_STYLE_OBLIQUE;
541                 pango_font_description_set_style(descr, style);
543                 newFontSpec = ConstructFontSpecification(descr);
544                 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
545                     // If the new font does not have even an oblique face, don't
546                     // allow italics to be set!
547                     newFontSpec = Glib::ustring("");
548                 }
550             } else {
551                 // If the new font does not have an italic face, don't
552                 // allow italics to be set!
553                 newFontSpec = Glib::ustring("");
554             }
555         }
557         pango_font_description_free(descr);
558     }
560     return newFontSpec;
563 /**
564     apply width property to the given font
565     @param fontSpec the given font
566     @param turnOn true to set bold
567     @return the changed fontspec, if the property can not be set return an empty string
568 */
569 Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn)
571     Glib::ustring newFontSpec;
573     // Find the PangoFontDesecription that goes with this font specification string
574     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
576     if (it != fontInstanceMap.end()) {
577         // If we did find one, make a copy and set/unset the bold as needed
578         PangoFontDescription *descr = pango_font_description_copy((*it).second);
580         PangoWeight weight;
581         if (turnOn) {
582             weight = PANGO_WEIGHT_BOLD;
583         } else {
584             weight = PANGO_WEIGHT_NORMAL;
585         }
586         pango_font_description_set_weight(descr, weight);
588         newFontSpec = ConstructFontSpecification(descr);
589         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
590             // If the new font does not have a bold face, don't
591             // allow bold to be set!
592             newFontSpec = Glib::ustring("");
593         }
595         pango_font_description_free(descr);
596     }
598     return newFontSpec;
601 /////
603 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
605     return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
608 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
610     g_assert(map);
612     if (map) {
614         // Gather the family names as listed by Pango
615         PangoFontFamily**  families = NULL;
616         int numFamilies = 0;
617         pango_font_map_list_families(fontServer, &families, &numFamilies);
619         for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) {
621             // Gather the styles for this family
622             PangoFontFace** faces = NULL;
623             int numFaces = 0;
624             pango_font_family_list_faces(families[currentFamily], &faces, &numFaces);
626             for (int currentFace=0; currentFace < numFaces; currentFace++) {
628                 // If the face has a name, describe it, and then use the
629                 // description to get the UI family and face strings
631                 if (pango_font_face_get_face_name(faces[currentFace]) == NULL) {
632                     continue;
633                 }
635                 PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
636                 if (faceDescr) {
637                     Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
638                     Glib::ustring styleUIName = GetUIStyleString(faceDescr);
640                     if (!familyUIName.empty() && !styleUIName.empty()) {
641                         // Find the right place to put the style information, adding
642                         // a map entry for the family name if it doesn't yet exist
644                         FamilyToStylesMap::iterator iter = map->find(familyUIName);
646                         if (iter == map->end()) {
647                             map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>()));
648                         }
650                         // Insert into the style list and save the info in the reference maps
651                         // only if the style does not yet exist
653                         bool exists = false;
654                         std::list<Glib::ustring> &styleList = (*map)[familyUIName];
656                         for (std::list<Glib::ustring>::iterator it=styleList.begin();
657                                  it != styleList.end();
658                                  it++) {
659                             if (*it == styleUIName) {
660                                 exists = true;
661                                 break;
662                             }
663                         }
665                         if (!exists) {
666                             styleList.push_back(styleUIName);
668                             // Add the string info needed in the reference maps
669                             fontStringMap.insert(
670                                     std::make_pair(
671                                             Glib::ustring(familyUIName) + Glib::ustring(styleUIName),
672                                             ConstructFontSpecification(faceDescr)));
673                             fontInstanceMap.insert(
674                                     std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
675                         } else {
676                             pango_font_description_free(faceDescr);
677                         }
678                     } else {
679                         pango_font_description_free(faceDescr);
680                     }
681                 }
682             }
683         }
685         // Sort the style lists
686         for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
687             (*iter).second.sort(StyleNameCompareInternal);
688         }
689     }
692 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
694     font_instance *font = NULL;
696     g_assert(style);
698     if (style) {
699         //  First try to use the font specification if it is set
700         if (style->text->font_specification.set
701             && style->text->font_specification.value
702             && *style->text->font_specification.value) {
704             font = FaceFromFontSpecification(style->text->font_specification.value);
705         }
707         // If that failed, try using the CSS information in the style
708         if (!font) {
709             font = Face(style->text->font_family.value, font_style_to_pos(*style));
710         }
711     }
713     return font;
716 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
718     PangoFontDescription *temp_descr = pango_font_description_from_string(style);
719     pango_font_description_set_family(temp_descr,family);
720     font_instance *res = Face(temp_descr);
721     pango_font_description_free(temp_descr);
722     return res;
725 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
727     font_instance *fontInstance = NULL;
729     g_assert(uiFamily && uiStyle);
730     if (uiFamily && uiStyle) {
731         Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle);
733         UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString);
735         if (uiToPangoIter != fontStringMap.end ()) {
736             PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second);
737             if (pangoToDescrIter != fontInstanceMap.end()) {
738                 // We found the pango description - now we can make a font_instance
739                 PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second);
740                 fontInstance = Face(tempDescr);
741                 pango_font_description_free(tempDescr);
742             }
743         }
744     }
746     return fontInstance;
749 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
751     font_instance *fontInstance = NULL;
753     g_assert(pangoString);
755     if (pangoString) {
756         PangoFontDescription *descr = NULL;
758         // First attempt to find the font specification in the reference map
759         PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString));
760         if (it != fontInstanceMap.end()) {
761             descr = pango_font_description_copy((*it).second);
762         }
764         // Or create a font description from the string - this may fail or
765         // produce unexpected results if the string does not have a good format
766         if (!descr) {
767             descr = pango_font_description_from_string(pangoString);
768         }
770         if (descr && (pango_font_description_get_family(descr) != NULL)) {
771             fontInstance = Face(descr);
772         }
774         if (descr) {
775             pango_font_description_free(descr);
776         }
777     }
779     return fontInstance;
782 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
784     font_instance *font = NULL;
786     g_assert(fontSpecification);
788     if (fontSpecification) {
789         // How the string is used to reconstruct a font depends on how it
790         // was constructed in ConstructFontSpecification.  As it stands,
791         // the font specification is a pango-created string
792         font = FaceFromPangoString(fontSpecification);
793     }
795     return font;
800 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
802 #ifdef USE_PANGO_WIN32
803     // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
804     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
805 #else
806     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
807 #endif
809     font_instance *res = NULL;
811     FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
812     if ( loadedFaces.find(descr) == loadedFaces.end() ) {
813         // not yet loaded
814         PangoFont *nFace = NULL;
816         // workaround for bug #1025565.
817         // fonts without families blow up Pango.
818         if (pango_font_description_get_family(descr) != NULL) {
819             nFace = pango_font_map_load_font(fontServer,fontContext,descr);
820         }
821         else {
822             g_warning(_("Ignoring font without family that will crash Pango"));
823         }
825         if ( nFace ) {
826             // duplicate FcPattern, the hard way
827             res = new font_instance();
828             // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at
829             // in the hash_map.  the descr of the returned pangofont may differ from what was asked, so we don't know (at this
830             // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems)
831             res->descr = pango_font_description_copy(descr);
832             res->daddy = this;
833             res->InstallFace(nFace);
834             if ( res->pFont == NULL ) {
835                 // failed to install face -> bitmap font
836                 // printf("face failed\n");
837                 res->daddy = NULL;
838                 delete res;
839                 res = NULL;
840                 if ( canFail ) {
841                     char *tc = pango_font_description_to_string(descr);
842                     PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
843                     g_free(tc);
844                     pango_font_description_set_family(descr,"Sans");
845                     res = Face(descr,false);
846                 }
847             } else {
848                 loadedFaces[res->descr]=res;
849                 res->Ref();
850                 AddInCache(res);
851             }
852         } else {
853             // no match
854             if ( canFail ) {
855                 PANGO_DEBUG("falling back to Sans\n");
856                 descr = pango_font_description_new();
857                 pango_font_description_set_family(descr,"Sans");
858                 res = Face(descr,false);
859                 pango_font_description_free(descr);
860             }
861         }
862     } else {
863         // already here
864         res = loadedFaces[descr];
865         res->Ref();
866         AddInCache(res);
867     }
868     if (res) {
869         res->InitTheFace();
870     }
871     return res;
874 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
876     PangoFontDescription *temp_descr = pango_font_description_new();
877     pango_font_description_set_family(temp_descr,family);
878     pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
879     pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
880     pango_font_description_set_style(temp_descr,(PangoStyle)style);
881     pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
882     font_instance *res = Face(temp_descr);
883     pango_font_description_free(temp_descr);
884     return res;
887 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
889     PangoFontDescription *temp_descr = pango_font_description_new();
891     pango_font_description_set_family(temp_descr, family);
893     if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
894         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
895     } else {
896         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
897     }
899     if ( apos.italic ) {
900         pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
901     } else if ( apos.oblique ) {
902         pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
903     } else {
904         pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
905     }
907     if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
908         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
909     } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
910         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
911     } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
912         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
913     } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
914         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
915     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
916         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
917     } else {
918         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
919     }
921     if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
922         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
923     } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
924         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
925     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
926         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
927     } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
928         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
929     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
930         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
931     } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
932         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
933     } else {
934         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
935     }
937     font_instance *res = Face(temp_descr);
938     pango_font_description_free(temp_descr);
939     return res;
942 void font_factory::UnrefFace(font_instance *who)
944     if ( who ) {
945         FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
947         if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
948             // not found
949             char *tc = pango_font_description_to_string(who->descr);
950             g_warning("unrefFace %p=%s: failed\n",who,tc);
951             g_free(tc);
952         } else {
953             loadedFaces.erase(loadedFaces.find(who->descr));
954             //                  printf("unrefFace %p: success\n",who);
955         }
956     }
959 void font_factory::AddInCache(font_instance *who)
961     if ( who == NULL ) return;
962     for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
963     for (int i = 0;i < nbEnt;i++) {
964         if ( ents[i].f == who ) {
965             //                  printf("present\n");
966             ents[i].age += 1.0;
967             return;
968         }
969     }
970     if ( nbEnt > maxEnt ) {
971         printf("cache sur-plein?\n");
972         return;
973     }
974     who->Ref();
975     if ( nbEnt == maxEnt ) {
976         int    bi = 0;
977         double ba = ents[bi].age;
978         for (int i = 1;i < nbEnt;i++) {
979             if ( ents[i].age < ba ) {
980                 bi = i;
981                 ba = ents[bi].age;
982             }
983         }
984         ents[bi].f->Unref();
985         ents[bi]=ents[--nbEnt];
986     }
987     ents[nbEnt].f = who;
988     ents[nbEnt].age = 1.0;
989     nbEnt++;
993 /*
994   Local Variables:
995   mode:c++
996   c-file-style:"stroustrup"
997   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
998   indent-tabs-mode:nil
999   fill-column:99
1000   End:
1001 */
1002 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :