Code

Super duper mega (fun!) commit: replaced encoding=utf-8 with fileencoding=utf-8 in...
[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 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <glibmm.h>
18 #include <glib/gmem.h>
19 #include <glibmm/i18n.h> // _()
20 #include <pango/pangoft2.h>
21 #include "libnrtype/FontFactory.h"
22 #include "libnrtype/font-instance.h"
23 #include "util/unordered-containers.h"
25 #if !PANGO_VERSION_CHECK(1,24,0)
26 #define PANGO_WEIGHT_THIN       static_cast<PangoWeight>(100)
27 #define PANGO_WEIGHT_BOOK       static_cast<PangoWeight>(380)
28 #define PANGO_WEIGHT_MEDIUM     static_cast<PangoWeight>(500)
29 #define PANGO_WEIGHT_ULTRAHEAVY static_cast<PangoWeight>(1000)
30 #endif
32 typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType;
34 // need to avoid using the size field
35 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
36     int h = 0;
37     h *= 1128467;
38     char const *theF = pango_font_description_get_family(x);
39     h += (theF)?g_str_hash(theF):0;
40     h *= 1128467;
41     h += (int)pango_font_description_get_style(x);
42     h *= 1128467;
43     h += (int)pango_font_description_get_variant(x);
44     h *= 1128467;
45     h += (int)pango_font_description_get_weight(x);
46     h *= 1128467;
47     h += (int)pango_font_description_get_stretch(x);
48     return h;
49 }
50 bool  font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const {
51     //if ( pango_font_description_equal(a,b) ) return true;
52     char const *fa = pango_font_description_get_family(a);
53     char const *fb = pango_font_description_get_family(b);
54     if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
55     if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
56     if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
57     if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
58     if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
59     if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
60     return true;
61 }
63 /////////////////// helper functions
65 /**
66  * A wrapper for strcasestr that also provides an implementation for Win32.
67  */
68 static bool
69 ink_strstr(char const *haystack, char const *pneedle)
70 {
71     // windows has no strcasestr implementation, so here is ours...
72     // stolen from nmap
73     /* FIXME: This is broken for e.g. ink_strstr("aab", "ab").  Report to nmap.
74      *
75      * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
76      * of tolower.  Given that haystack is a font name (i.e. fairly short), it should be ok to
77      * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
78      *
79      * Rather than fixing in inkscape, consider getting rid of this routine, instead using
80      * strdown and plain strstr at caller.  We have control over the needle values, so we can
81      * modify the callers rather than calling strdown there.
82      */
83     char buf[512];
84     register char const *p;
85     char *needle, *q, *foundto;
86     if (!*pneedle) return true;
87     if (!haystack) return false;
89     needle = buf;
90     p = pneedle; q = needle;
91     while ((*q++ = tolower(*p++)))
92         ;
93     p = haystack - 1; foundto = needle;
94     while (*++p) {
95         if (tolower(*p) == *foundto) {
96             if (!*++foundto) {
97                 /* Yeah, we found it */
98                 return true;
99             }
100         } else foundto = needle;
101     }
102     return false;
105 /**
106  * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
107  */
108 // FIXME: make this UTF8, add non-English style names
109 static bool
110 is_regular(char const *s)
112     if (ink_strstr(s, "Regular")) return true;
113     if (ink_strstr(s, "Roman")) return true;
114     if (ink_strstr(s, "Normal")) return true;
115     if (ink_strstr(s, "Plain")) return true;
116     return false;
119 /**
120  * Non-bold fonts are 'Medium' or 'Book'
121  */
122 static bool
123 is_nonbold(char const *s)
125     if (ink_strstr(s, "Medium")) return true;
126     if (ink_strstr(s, "Book")) return true;
127     return false;
130 /**
131  * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
132  */
133 static bool
134 is_italic(char const *s)
136     if (ink_strstr(s, "Italic")) return true;
137     if (ink_strstr(s, "Oblique")) return true;
138     if (ink_strstr(s, "Slanted")) return true;
139     return false;
142 /**
143  * Bold fonts are 'Bold'
144  */
145 static bool
146 is_bold(char const *s)
148     if (ink_strstr(s, "Bold")) return true;
149     return false;
152 /**
153  * Caps fonts are 'Caps'
154  */
155 static bool
156 is_caps(char const *s)
158     if (ink_strstr(s, "Caps")) return true;
159     return false;
162 #if 0 /* FIXME: These are all unused.  Please delete them or use them (presumably in
163 * style_name_compare). */
164 /**
165  * Monospaced fonts are 'Mono'
166  */
167 static bool
168 is_mono(char const *s)
170     if (ink_strstr(s, "Mono")) return true;
171     return false;
174 /**
175  * Rounded fonts are 'Round'
176  */
177 static bool
178 is_round(char const *s)
180     if (ink_strstr(s, "Round")) return true;
181     return false;
184 /**
185  * Outline fonts are 'Outline'
186  */
187 static bool
188 is_outline(char const *s)
190     if (ink_strstr(s, "Outline")) return true;
191     return false;
194 /**
195  * Swash fonts are 'Swash'
196  */
197 static bool
198 is_swash(char const *s)
200     if (ink_strstr(s, "Swash")) return true;
201     return false;
203 #endif
205 /**
206  * Determines if two style names match.  This allows us to match
207  * based on the type of style rather than simply doing string matching,
208  * because for instance 'Plain' and 'Normal' mean the same thing.
209  *
210  * Q:  Shouldn't this include the other tests such as is_outline, etc.?
211  * Q:  Is there a problem with strcasecmp on Win32?  Should it use stricmp?
212  */
213 int
214 style_name_compare(char const *aa, char const *bb)
216     char const *a = (char const *) aa;
217     char const *b = (char const *) bb;
219     if (is_regular(a) && !is_regular(b)) return -1;
220     if (is_regular(b) && !is_regular(a)) return 1;
222     if (is_bold(a) && !is_bold(b)) return 1;
223     if (is_bold(b) && !is_bold(a)) return -1;
225     if (is_italic(a) && !is_italic(b)) return 1;
226     if (is_italic(b) && !is_italic(a)) return -1;
228     if (is_nonbold(a) && !is_nonbold(b)) return 1;
229     if (is_nonbold(b) && !is_nonbold(a)) return -1;
231     if (is_caps(a) && !is_caps(b)) return 1;
232     if (is_caps(b) && !is_caps(a)) return -1;
234     return strcasecmp(a, b);
237 /*
238  defined but not used:
240 static int
241 style_record_compare(void const *aa, void const *bb)
243     NRStyleRecord const *a = (NRStyleRecord const *) aa;
244     NRStyleRecord const *b = (NRStyleRecord const *) bb;
246     return (style_name_compare(a->name, b->name));
249 static void font_factory_name_list_destructor(NRNameList *list)
251     for (unsigned int i = 0; i < list->length; i++)
252         g_free(list->names[i]);
253     if ( list->names ) g_free(list->names);
256 static void font_factory_style_list_destructor(NRStyleList *list)
258     for (unsigned int i = 0; i < list->length; i++) {
259         g_free((void *) (list->records)[i].name);
260         g_free((void *) (list->records)[i].descr);
261     }
262     if ( list->records ) g_free(list->records);
264 */
266 /**
267  * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
268  */
269 int
270 family_name_compare(char const *a, char const *b)
272 #ifndef WIN32
273     return strcasecmp((*((char const **) a)), (*((char const **) b)));
274 #else
275     return stricmp((*((char const **) a)), (*((char const **) b)));
276 #endif
279 void noop(...) {}
280 //#define PANGO_DEBUG g_print
281 #define PANGO_DEBUG noop
285 ///////////////////// FontFactory
286 #ifndef USE_PANGO_WIN32
287 // the substitute function to tell fontconfig to enforce outline fonts
288 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
290     FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
291     //char *fam = NULL;
292     //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
293     //printf("subst_f on %s\n",fam);
295 #endif
298 font_factory *font_factory::lUsine = NULL;
300 font_factory *font_factory::Default(void)
302     if ( lUsine == NULL ) lUsine = new font_factory;
303     return lUsine;
306 font_factory::font_factory(void) :
307     nbEnt(0),
308     maxEnt(32),
309     ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))),
311 #ifdef USE_PANGO_WIN32
312     fontServer(pango_win32_font_map_for_display()),
313     fontContext(pango_win32_get_context()),
314     pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)),
315     hScreenDC(pango_win32_get_dc()),
316 #else
317     fontServer(pango_ft2_font_map_new()),
318     fontContext(0),
319 #endif
320     fontSize(512),
321     loadedPtr(new FaceMapType())
323 #ifdef USE_PANGO_WIN32
324 #else
325     pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
326     fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
327     pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
328 #endif
331 font_factory::~font_factory(void)
333     if (loadedPtr) {
334         FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr);
335         delete tmp;
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 /**
432     Replace font family leaving style alone (if possible).
433     @param fontSpec the given font
434     @param newFamily
435     @return the changed fontspec, if the property can not be set return an empty string
436     The routine first searches for an exact match.
437     If no exact match found, calls FontSpecificationBestMatch().
438 */
439 Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily)
441     Glib::ustring newFontSpec;
443     // Although we are using the string from pango_font_description_to_string for the
444     // font specification, we definitely cannot just set the new family in the
445     // PangoFontDescription structure and ask for a new string.  This is because
446     // what constitutes a "family" in our own UI may be different from how Pango
447     // sees it.
449     // Find the PangoFontDescription associated with the font specification string.
450     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
453     if (it != fontInstanceMap.end()) {
454         // Description found!
456         // Make copy
457         PangoFontDescription *descr = pango_font_description_copy((*it).second);
459         // Grab the UI Family string from the descr
460         Glib::ustring uiFamily = GetUIFamilyString(descr);
462         // Replace the UI Family name with the new family name
463         std::size_t found = fontSpec.find(uiFamily);
464         if (found != Glib::ustring::npos) {
465             newFontSpec = fontSpec;
466             newFontSpec.erase(found, uiFamily.size());
467             newFontSpec.insert(found, newFamily);
469             // If the new font specification does not exist in the reference maps,
470             // search for the next best match for the faces in that style
471             it = fontInstanceMap.find(newFontSpec);
472             if (it == fontInstanceMap.end()) {
474                 // Search for best match, empty string returned if not found.
475                 newFontSpec = FontSpecificationBestMatch( newFontSpec );
477             }
478         }
480         pango_font_description_free(descr);
481     }
483     return newFontSpec;
486 /**
487     Apply style property to the given font
488     @param fontSpec the given font
489     @param turnOn true to set italic style
490     @return the changed fontspec, if the property can not be set return an empty string
491     The routine first searches for an exact match to "FontFamily Italic" or
492     "Font Family Oblique" (turnOn is true) or "FontFamily" (turnOn is false).
493     If no exact match found, calls FontSpecificationBestMatch().
494 */
495 Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn)
497     Glib::ustring newFontSpec;
499     // Find the PangoFontDescription associated with the font specification string.
500     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
502     if (it != fontInstanceMap.end()) {
503         // Description found!
505         // Make copy.
506         PangoFontDescription *descr = pango_font_description_copy((*it).second);
508         PangoStyle style;
509         if (turnOn) {
510             // First try Oblique, we'll try Italic later
511             style = PANGO_STYLE_OBLIQUE;
512         } else {
513             style = PANGO_STYLE_NORMAL;
514         }
516         pango_font_description_set_style(descr, style);
518         newFontSpec = ConstructFontSpecification(descr);
520         bool exactMatchFound = true;
521         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
523             exactMatchFound = false;
524             if (turnOn) {
525                 // Next try Italic
526                 style = PANGO_STYLE_ITALIC;
527                 pango_font_description_set_style(descr, style);
529                 exactMatchFound = true;
530                 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
531                     exactMatchFound = false;
532                 }
533             }
534         }
536         // Search for best match, empty string returned if not found.
537         if( !exactMatchFound ) {
538            newFontSpec = FontSpecificationBestMatch( newFontSpec );
539         }
541         pango_font_description_free(descr);
542     }
544     return newFontSpec; // Empty if not found.
547 /**
548     Apply weight property to the given font
549     @param fontSpec the given font
550     @param turnOn true to set bold
551     @return the changed fontspec, if the property can not be set return an empty string
552     This routine first searches for an exact match, if none found
553     it calls FontSpecificationBestMatch().
554 */
555 Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn)
557     Glib::ustring newFontSpec;
559     // Find the PangoFontDescription associated with the font specification string.
560     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
562     if (it != fontInstanceMap.end()) {
563         // Description found!
565         // Make copy.
566         PangoFontDescription *descr = pango_font_description_copy((*it).second);
569         PangoWeight weight;
570         if (turnOn) {
571             weight = PANGO_WEIGHT_BOLD;
572         } else {
573             weight = PANGO_WEIGHT_NORMAL;
574         }
576         pango_font_description_set_weight(descr, weight);
578         newFontSpec = ConstructFontSpecification(descr);
580         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
581             // Search for best match, empty string returned if not found.
582             newFontSpec = FontSpecificationBestMatch( newFontSpec );
583         }
585         pango_font_description_free(descr);
586     }
588     return newFontSpec; // Empty if not found.
591 /**
592     Use pango_font_description_better_match() to find best font match.
593     This handles cases like Century Schoolbook L where the "normal"
594     font is Century Schoolbook L Medium so just removing Italic
595     from the font name doesn't yield the correct name.
596     @param fontSpec the given font
597     @return the changed fontspec, if the property can not be set return an empty string
598 */
599 // http://library.gnome.org/devel/pango/1.28/pango-Fonts.html#pango-font-description-better-match
600 Glib::ustring font_factory::FontSpecificationBestMatch(const Glib::ustring & fontSpec )
603     Glib::ustring newFontSpec;
605     // Look for exact match
606     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
608     // If there is no exact match, look for the best match.
609     if (it != fontInstanceMap.end()) {
611         newFontSpec = fontSpec;
613     } else {
615         PangoFontDescription *fontDescr = pango_font_description_from_string(fontSpec.c_str());
616         PangoFontDescription *bestMatchDescr = NULL;
618         // Grab the UI Family string from the descr
619         Glib::ustring family = GetUIFamilyString(fontDescr);
620         Glib::ustring bestMatchDescription;
622         bool setFirstFamilyMatch = false;
623         for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
625             Glib::ustring currentFontSpec = (*it).first;
626             Glib::ustring currentFamily = GetUIFamilyString((*it).second);
628             // Save some time by only looking at the right family.
629             // Must use family name rather than fontSpec
630             //   (otherwise DejaVu Sans matches DejaVu Sans Mono).
631             if (currentFamily == family) {
632                 if (!setFirstFamilyMatch) {
633                     // This ensures that the closest match is at least within the correct
634                     // family rather than the first font in the list
635                     bestMatchDescr = pango_font_description_copy((*it).second);
636                     bestMatchDescription = currentFontSpec;
637                     setFirstFamilyMatch = true;
638                 } else {
639                     // Get the font description that corresponds, and
640                     // then see if we've found a better match
641                     PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second);
643                     if (pango_font_description_better_match(
644                             fontDescr, bestMatchDescr, possibleMatch)) {
646                         pango_font_description_free(bestMatchDescr);
647                         bestMatchDescr = possibleMatch;
648                         bestMatchDescription = currentFontSpec;
649                     } else {
650                         pango_font_description_free(possibleMatch);
651                     }
652                 }
653             }
654         } // for
656         newFontSpec = bestMatchDescription; // If NULL, then no match found
658         pango_font_description_free(fontDescr);
659         pango_font_description_free(bestMatchDescr);
661     }
663     return newFontSpec;
666 /////
668 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
670     return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
673 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
675     g_assert(map);
677     if (map) {
679         // Gather the family names as listed by Pango
680         PangoFontFamily**  families = NULL;
681         int numFamilies = 0;
682         pango_font_map_list_families(fontServer, &families, &numFamilies);
684         for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) {
686             // Gather the styles for this family
687             PangoFontFace** faces = NULL;
688             int numFaces = 0;
689             pango_font_family_list_faces(families[currentFamily], &faces, &numFaces);
691             for (int currentFace=0; currentFace < numFaces; currentFace++) {
693                 // If the face has a name, describe it, and then use the
694                 // description to get the UI family and face strings
696                 if (pango_font_face_get_face_name(faces[currentFace]) == NULL) {
697                     continue;
698                 }
700                 PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
701                 if (faceDescr) {
702                     Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
703                     Glib::ustring styleUIName = GetUIStyleString(faceDescr);
705                     if (!familyUIName.empty() && !styleUIName.empty()) {
706                         // Find the right place to put the style information, adding
707                         // a map entry for the family name if it doesn't yet exist
709                         FamilyToStylesMap::iterator iter = map->find(familyUIName);
711                         if (iter == map->end()) {
712                             map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>()));
713                         }
715                         // Insert into the style list and save the info in the reference maps
716                         // only if the style does not yet exist
718                         bool exists = false;
719                         std::list<Glib::ustring> &styleList = (*map)[familyUIName];
721                         for (std::list<Glib::ustring>::iterator it=styleList.begin();
722                                  it != styleList.end();
723                                  it++) {
724                             if (*it == styleUIName) {
725                                 exists = true;
726                                 break;
727                             }
728                         }
730                         if (!exists) {
731                             styleList.push_back(styleUIName);
733                             // Add the string info needed in the reference maps
734                             fontStringMap.insert(
735                                     std::make_pair(
736                                             Glib::ustring(familyUIName) + Glib::ustring(styleUIName),
737                                             ConstructFontSpecification(faceDescr)));
738                             fontInstanceMap.insert(
739                                     std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
740                         } else {
741                             pango_font_description_free(faceDescr);
742                         }
743                     } else {
744                         pango_font_description_free(faceDescr);
745                     }
746                 }
747             }
748         }
750         // Sort the style lists
751         for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
752             (*iter).second.sort(StyleNameCompareInternal);
753         }
754     }
757 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
759     font_instance *font = NULL;
761     g_assert(style);
763     if (style) {
765         //  First try to use the font specification if it is set
766         if (style->text->font_specification.set
767             && style->text->font_specification.value
768             && *style->text->font_specification.value) {
770             font = FaceFromFontSpecification(style->text->font_specification.value);
771         }
773         // If that failed, try using the CSS information in the style
774         if (!font) {
776             font = Face(style->text->font_family.value, font_style_to_pos(*style));
778             // That was a hatchet job... so we need to check if this font exists!!
779             Glib::ustring fontSpec = font_factory::Default()->ConstructFontSpecification(font);
780             Glib::ustring newFontSpec = FontSpecificationBestMatch( fontSpec );
781             if( fontSpec != newFontSpec ) {
782                 font->Unref();
783                 font = FaceFromFontSpecification( newFontSpec.c_str() );
784             }
785         }
786     }
788     return font;
791 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
793     PangoFontDescription *temp_descr = pango_font_description_from_string(style);
794     pango_font_description_set_family(temp_descr,family);
795     font_instance *res = Face(temp_descr);
796     pango_font_description_free(temp_descr);
797     return res;
800 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
802     font_instance *fontInstance = NULL;
804     g_assert(uiFamily && uiStyle);
805     if (uiFamily && uiStyle) {
806         Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle);
808         UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString);
810         if (uiToPangoIter != fontStringMap.end ()) {
811             PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second);
812             if (pangoToDescrIter != fontInstanceMap.end()) {
813                 // We found the pango description - now we can make a font_instance
814                 PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second);
815                 fontInstance = Face(tempDescr);
816                 pango_font_description_free(tempDescr);
817             }
818         }
819     }
821     return fontInstance;
824 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
826     font_instance *fontInstance = NULL;
828     g_assert(pangoString);
830     if (pangoString) {
831         PangoFontDescription *descr = NULL;
833         // First attempt to find the font specification in the reference map
834         PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString));
835         if (it != fontInstanceMap.end()) {
836             descr = pango_font_description_copy((*it).second);
837         }
839         // Or create a font description from the string - this may fail or
840         // produce unexpected results if the string does not have a good format
841         if (!descr) {
842             descr = pango_font_description_from_string(pangoString);
843         }
845         if (descr && (pango_font_description_get_family(descr) != NULL)) {
846             fontInstance = Face(descr);
847         }
849         if (descr) {
850             pango_font_description_free(descr);
851         }
852     }
854     return fontInstance;
857 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
859     font_instance *font = NULL;
861     g_assert(fontSpecification);
863     if (fontSpecification) {
864         // How the string is used to reconstruct a font depends on how it
865         // was constructed in ConstructFontSpecification.  As it stands,
866         // the font specification is a pango-created string
867         font = FaceFromPangoString(fontSpecification);
868     }
870     return font;
875 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
877 #ifdef USE_PANGO_WIN32
878     // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
879     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
880 #else
881     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
882 #endif
884     font_instance *res = NULL;
886     FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
887     if ( loadedFaces.find(descr) == loadedFaces.end() ) {
888         // not yet loaded
889         PangoFont *nFace = NULL;
891         // workaround for bug #1025565.
892         // fonts without families blow up Pango.
893         if (pango_font_description_get_family(descr) != NULL) {
894             nFace = pango_font_map_load_font(fontServer,fontContext,descr);
895         }
896         else {
897             g_warning(_("Ignoring font without family that will crash Pango"));
898         }
900         if ( nFace ) {
901             // duplicate FcPattern, the hard way
902             res = new font_instance();
903             // store the descr of the font we asked for, since this is the key where we intend
904             // to put the font_instance at in the unordered_map.  the descr of the returned
905             // pangofont may differ from what was asked, so we don't know (at this
906             // point) whether loadedFaces[that_descr] is free or not (and overwriting
907             // an entry will bring deallocation problems)
908             res->descr = pango_font_description_copy(descr);
909             res->daddy = this;
910             res->InstallFace(nFace);
911             if ( res->pFont == NULL ) {
912                 // failed to install face -> bitmap font
913                 // printf("face failed\n");
914                 res->daddy = NULL;
915                 delete res;
916                 res = NULL;
917                 if ( canFail ) {
918                     char *tc = pango_font_description_to_string(descr);
919                     PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
920                     g_free(tc);
921                     pango_font_description_set_family(descr,"Sans");
922                     res = Face(descr,false);
923                 }
924             } else {
925                 loadedFaces[res->descr]=res;
926                 res->Ref();
927                 AddInCache(res);
928             }
929         } else {
930             // no match
931             if ( canFail ) {
932                 PANGO_DEBUG("falling back to Sans\n");
933                 descr = pango_font_description_new();
934                 pango_font_description_set_family(descr,"Sans");
935                 res = Face(descr,false);
936                 pango_font_description_free(descr);
937             }
938         }
939     } else {
940         // already here
941         res = loadedFaces[descr];
942         res->Ref();
943         AddInCache(res);
944     }
945     if (res) {
946         res->InitTheFace();
947     }
948     return res;
951 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
953     PangoFontDescription *temp_descr = pango_font_description_new();
954     pango_font_description_set_family(temp_descr,family);
955     pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
956     pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
957     pango_font_description_set_style(temp_descr,(PangoStyle)style);
958     pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
959     font_instance *res = Face(temp_descr);
960     pango_font_description_free(temp_descr);
961     return res;
964 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
966     PangoFontDescription *temp_descr = pango_font_description_new();
968     pango_font_description_set_family(temp_descr, family);
970     if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
971         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
972     } else {
973         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
974     }
976     if ( apos.italic ) {
977         pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
978     } else if ( apos.oblique ) {
979         pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
980     } else {
981         pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
982     }
984     if ( apos.weight <= NR_POS_WEIGHT_THIN ) {
985         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_THIN);
986     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
987         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
988     } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
989         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
990     } else if ( apos.weight <= NR_POS_WEIGHT_BOOK ) {
991         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOOK);
992     } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
993         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
994     } else if ( apos.weight <= NR_POS_WEIGHT_MEDIUM ) {
995         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_MEDIUM);
996     } else if ( apos.weight <= NR_POS_WEIGHT_SEMIBOLD ) {
997         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_SEMIBOLD);
998     } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
999         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
1000     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
1001         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
1002     } else {
1003         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
1004     }
1005     // PANGO_WIEGHT_ULTRAHEAVY not used (not CSS2)
1007     if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
1008         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
1009     } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
1010         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
1011     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
1012         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
1013     } else if ( apos.stretch <= NR_POS_STRETCH_NORMAL ) {
1014         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
1015     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
1016         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
1017     } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
1018         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
1019     } else {
1020         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
1021     }
1023     font_instance *res = Face(temp_descr);
1024     pango_font_description_free(temp_descr);
1025     return res;
1028 void font_factory::UnrefFace(font_instance *who)
1030     if ( who ) {
1031         FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
1033         if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
1034             // not found
1035             char *tc = pango_font_description_to_string(who->descr);
1036             g_warning("unrefFace %p=%s: failed\n",who,tc);
1037             g_free(tc);
1038         } else {
1039             loadedFaces.erase(loadedFaces.find(who->descr));
1040             //            printf("unrefFace %p: success\n",who);
1041         }
1042     }
1045 void font_factory::AddInCache(font_instance *who)
1047     if ( who == NULL ) return;
1048     for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
1049     for (int i = 0;i < nbEnt;i++) {
1050         if ( ents[i].f == who ) {
1051             //            printf("present\n");
1052             ents[i].age += 1.0;
1053             return;
1054         }
1055     }
1056     if ( nbEnt > maxEnt ) {
1057         printf("cache sur-plein?\n");
1058         return;
1059     }
1060     who->Ref();
1061     if ( nbEnt == maxEnt ) {
1062         int    bi = 0;
1063         double ba = ents[bi].age;
1064         for (int i = 1;i < nbEnt;i++) {
1065             if ( ents[i].age < ba ) {
1066                 bi = i;
1067                 ba = ents[bi].age;
1068             }
1069         }
1070         ents[bi].f->Unref();
1071         ents[bi]=ents[--nbEnt];
1072     }
1073     ents[nbEnt].f = who;
1074     ents[nbEnt].age = 1.0;
1075     nbEnt++;
1078 /*
1079         {
1080             std::cout << " Printing out fontInstanceMap: " << std::endl;
1081             PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
1082             while (it != fontInstanceMap.end()) {
1084                 PangoFontDescription *descr = pango_font_description_copy((*it).second);
1086                 // Grab the UI Family string from the descr
1087                 Glib::ustring uiFamily = GetUIFamilyString(descr);
1088                 Glib::ustring uiStyle  = GetUIStyleString(descr);
1089                 std::cout << "     " << uiFamily << "  " << uiStyle << std::endl;
1091                 it++;
1092             }
1093         }
1094 */
1096 /*
1097   Local Variables:
1098   mode:c++
1099   c-file-style:"stroustrup"
1100   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1101   indent-tabs-mode:nil
1102   fill-column:99
1103   End:
1104 */
1105 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :