Code

06fb93f2da2ed888f959cf66c3cd9efbff81a59f
[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 typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType;
27 // need to avoid using the size field
28 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
29     int h = 0;
30     h *= 1128467;
31     char const *theF = pango_font_description_get_family(x);
32     h += (theF)?g_str_hash(theF):0;
33     h *= 1128467;
34     h += (int)pango_font_description_get_style(x);
35     h *= 1128467;
36     h += (int)pango_font_description_get_variant(x);
37     h *= 1128467;
38     h += (int)pango_font_description_get_weight(x);
39     h *= 1128467;
40     h += (int)pango_font_description_get_stretch(x);
41     return h;
42 }
43 bool  font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const {
44     //if ( pango_font_description_equal(a,b) ) return true;
45     char const *fa = pango_font_description_get_family(a);
46     char const *fb = pango_font_description_get_family(b);
47     if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
48     if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
49     if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
50     if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
51     if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
52     if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
53     return true;
54 }
56 /////////////////// helper functions
58 /**
59  * A wrapper for strcasestr that also provides an implementation for Win32.
60  */
61 static bool
62 ink_strstr(char const *haystack, char const *pneedle)
63 {
64     // windows has no strcasestr implementation, so here is ours...
65     // stolen from nmap
66     /* FIXME: This is broken for e.g. ink_strstr("aab", "ab").  Report to nmap.
67      *
68      * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
69      * of tolower.  Given that haystack is a font name (i.e. fairly short), it should be ok to
70      * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
71      *
72      * Rather than fixing in inkscape, consider getting rid of this routine, instead using
73      * strdown and plain strstr at caller.  We have control over the needle values, so we can
74      * modify the callers rather than calling strdown there.
75      */
76     char buf[512];
77     register char const *p;
78     char *needle, *q, *foundto;
79     if (!*pneedle) return true;
80     if (!haystack) return false;
82     needle = buf;
83     p = pneedle; q = needle;
84     while ((*q++ = tolower(*p++)))
85         ;
86     p = haystack - 1; foundto = needle;
87     while (*++p) {
88         if (tolower(*p) == *foundto) {
89             if (!*++foundto) {
90                 /* Yeah, we found it */
91                 return true;
92             }
93         } else foundto = needle;
94     }
95     return false;
96 }
98 /**
99  * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
100  */
101 // FIXME: make this UTF8, add non-English style names
102 static bool
103 is_regular(char const *s)
105     if (ink_strstr(s, "Regular")) return true;
106     if (ink_strstr(s, "Roman")) return true;
107     if (ink_strstr(s, "Normal")) return true;
108     if (ink_strstr(s, "Plain")) return true;
109     return false;
112 /**
113  * Non-bold fonts are 'Medium' or 'Book'
114  */
115 static bool
116 is_nonbold(char const *s)
118     if (ink_strstr(s, "Medium")) return true;
119     if (ink_strstr(s, "Book")) return true;
120     return false;
123 /**
124  * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
125  */
126 static bool
127 is_italic(char const *s)
129     if (ink_strstr(s, "Italic")) return true;
130     if (ink_strstr(s, "Oblique")) return true;
131     if (ink_strstr(s, "Slanted")) return true;
132     return false;
135 /**
136  * Bold fonts are 'Bold'
137  */
138 static bool
139 is_bold(char const *s)
141     if (ink_strstr(s, "Bold")) return true;
142     return false;
145 /**
146  * Caps fonts are 'Caps'
147  */
148 static bool
149 is_caps(char const *s)
151     if (ink_strstr(s, "Caps")) return true;
152     return false;
155 #if 0 /* FIXME: These are all unused.  Please delete them or use them (presumably in
156 * style_name_compare). */
157 /**
158  * Monospaced fonts are 'Mono'
159  */
160 static bool
161 is_mono(char const *s)
163     if (ink_strstr(s, "Mono")) return true;
164     return false;
167 /**
168  * Rounded fonts are 'Round'
169  */
170 static bool
171 is_round(char const *s)
173     if (ink_strstr(s, "Round")) return true;
174     return false;
177 /**
178  * Outline fonts are 'Outline'
179  */
180 static bool
181 is_outline(char const *s)
183     if (ink_strstr(s, "Outline")) return true;
184     return false;
187 /**
188  * Swash fonts are 'Swash'
189  */
190 static bool
191 is_swash(char const *s)
193     if (ink_strstr(s, "Swash")) return true;
194     return false;
196 #endif
198 /**
199  * Determines if two style names match.  This allows us to match
200  * based on the type of style rather than simply doing string matching,
201  * because for instance 'Plain' and 'Normal' mean the same thing.
202  *
203  * Q:  Shouldn't this include the other tests such as is_outline, etc.?
204  * Q:  Is there a problem with strcasecmp on Win32?  Should it use stricmp?
205  */
206 int
207 style_name_compare(char const *aa, char const *bb)
209     char const *a = (char const *) aa;
210     char const *b = (char const *) bb;
212     if (is_regular(a) && !is_regular(b)) return -1;
213     if (is_regular(b) && !is_regular(a)) return 1;
215     if (is_bold(a) && !is_bold(b)) return 1;
216     if (is_bold(b) && !is_bold(a)) return -1;
218     if (is_italic(a) && !is_italic(b)) return 1;
219     if (is_italic(b) && !is_italic(a)) return -1;
221     if (is_nonbold(a) && !is_nonbold(b)) return 1;
222     if (is_nonbold(b) && !is_nonbold(a)) return -1;
224     if (is_caps(a) && !is_caps(b)) return 1;
225     if (is_caps(b) && !is_caps(a)) return -1;
227     return strcasecmp(a, b);
230 /*
231  defined but not used:
233 static int
234 style_record_compare(void const *aa, void const *bb)
236     NRStyleRecord const *a = (NRStyleRecord const *) aa;
237     NRStyleRecord const *b = (NRStyleRecord const *) bb;
239     return (style_name_compare(a->name, b->name));
242 static void font_factory_name_list_destructor(NRNameList *list)
244     for (unsigned int i = 0; i < list->length; i++)
245         g_free(list->names[i]);
246     if ( list->names ) g_free(list->names);
249 static void font_factory_style_list_destructor(NRStyleList *list)
251     for (unsigned int i = 0; i < list->length; i++) {
252         g_free((void *) (list->records)[i].name);
253         g_free((void *) (list->records)[i].descr);
254     }
255     if ( list->records ) g_free(list->records);
257 */
259 /**
260  * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
261  */
262 int
263 family_name_compare(char const *a, char const *b)
265 #ifndef WIN32
266     return strcasecmp((*((char const **) a)), (*((char const **) b)));
267 #else
268     return stricmp((*((char const **) a)), (*((char const **) b)));
269 #endif
272 void noop(...) {}
273 //#define PANGO_DEBUG g_print
274 #define PANGO_DEBUG noop
278 ///////////////////// FontFactory
279 #ifndef USE_PANGO_WIN32
280 // the substitute function to tell fontconfig to enforce outline fonts
281 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
283     FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
284     //char *fam = NULL;
285     //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
286     //printf("subst_f on %s\n",fam);
288 #endif
291 font_factory *font_factory::lUsine = NULL;
293 font_factory *font_factory::Default(void)
295     if ( lUsine == NULL ) lUsine = new font_factory;
296     return lUsine;
299 font_factory::font_factory(void) :
300     nbEnt(0),
301     maxEnt(32),
302     ents(static_cast<font_entry*>(g_malloc(maxEnt*sizeof(font_entry)))),
304 #ifdef USE_PANGO_WIN32
305     fontServer(pango_win32_font_map_for_display()),
306     fontContext(pango_win32_get_context()),
307     pangoFontCache(pango_win32_font_map_get_font_cache(fontServer)),
308     hScreenDC(pango_win32_get_dc()),
309 #else
310     fontServer(pango_ft2_font_map_new()),
311     fontContext(0),
312 #endif
313     fontSize(512),
314     loadedPtr(new FaceMapType())
316 #ifdef USE_PANGO_WIN32
317 #else
318     pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
319     fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
320     pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
321 #endif
324 font_factory::~font_factory(void)
326     if (loadedPtr) {
327         FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr);
328         delete tmp;
329         loadedPtr = 0;
330     }
332     for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
333     if ( ents ) g_free(ents);
335     g_object_unref(fontServer);
336 #ifdef USE_PANGO_WIN32
337     pango_win32_shutdown_display();
338 #else
339     //pango_ft2_shutdown_display();
340 #endif
341     //g_object_unref(fontContext);
343     // Delete the pango font pointers in the string to instance map
344     PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
345     while (it != fontInstanceMap.end()) {
346         pango_font_description_free((*it).second);
347         it++;
348     }
352 Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font)
354     Glib::ustring pangoString;
356     g_assert(font);
358     if (font) {
359         // Once the format for the font specification is decided, it must be
360         // kept.. if it is absolutely necessary to change it, the attribute
361         // it is written to needs to have a new version so the legacy files
362         // can be read.
364         PangoFontDescription *copy = pango_font_description_copy(font);
366         pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE);
367         pangoString = Glib::ustring(pango_font_description_to_string(copy));
369         pango_font_description_free(copy);
371     }
373     return pangoString;
376 Glib::ustring font_factory::ConstructFontSpecification(font_instance *font)
378     Glib::ustring pangoString;
380     g_assert(font);
382     if (font) {
383         pangoString = ConstructFontSpecification(font->descr);
384     }
386     return pangoString;
389 Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr)
391     Glib::ustring family;
393     g_assert(fontDescr);
395     if (fontDescr) {
396         // For now, keep it as family name taken from pango
397         family = pango_font_description_get_family(fontDescr);
398     }
400     return family;
403 Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr)
405     Glib::ustring style;
407     g_assert(fontDescr);
409     if (fontDescr) {
410         PangoFontDescription *fontDescrCopy = pango_font_description_copy(fontDescr);
412         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_FAMILY);
413         pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_SIZE);
415         // For now, keep it as style name taken from pango
416         style = pango_font_description_to_string(fontDescrCopy);
418         pango_font_description_free(fontDescrCopy);
419     }
421     return style;
424 Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily)
426     Glib::ustring newFontSpec;
428     // Although we are using the string from pango_font_description_to_string for the
429     // font specification, we definitely cannot just set the new family in the
430     // PangoFontDescription structure and ask for a new string.  This is because
431     // what constitutes a "family" in our own UI may be different from how Pango
432     // sees it.
434     // Find the PangoFontDescription associated to this fontSpec
435     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
437     if (it != fontInstanceMap.end()) {
438         PangoFontDescription *descr = pango_font_description_copy((*it).second);
440         // Grab the UI Family string from the descr
441         Glib::ustring uiFamily = GetUIFamilyString(descr);
443         // Replace the UI Family name with the new family name
444         std::size_t found = fontSpec.find(uiFamily);
445         if (found != Glib::ustring::npos) {
446             newFontSpec = fontSpec;
447             newFontSpec.erase(found, uiFamily.size());
448             newFontSpec.insert(found, newFamily);
450             // If the new font specification does not exist in the reference maps,
451             // search for the next best match for the faces in that style
452             it = fontInstanceMap.find(newFontSpec);
453             if (it == fontInstanceMap.end()) {
455                 PangoFontDescription *newFontDescr = pango_font_description_from_string(newFontSpec.c_str());
457                 PangoFontDescription *bestMatchForNewDescr = NULL;
458                 Glib::ustring bestMatchFontDescription;
460                 bool setFirstFamilyMatch = false;
461                 for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
463                     Glib::ustring currentFontSpec = (*it).first;
465                     // Save some time by only looking at the right family
466                     if (currentFontSpec.find(newFamily) != Glib::ustring::npos) {
467                         if (!setFirstFamilyMatch) {
468                                 // This ensures that the closest match is at least within the correct
469                                 // family rather than the first font in the list
470                             bestMatchForNewDescr = pango_font_description_copy((*it).second);
471                             bestMatchFontDescription = currentFontSpec;
472                             setFirstFamilyMatch = true;
473                         } else {
474                             // Get the font description that corresponds, and
475                             // then see if we've found a better match
476                             PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second);
478                             if (pango_font_description_better_match(
479                                     newFontDescr, bestMatchForNewDescr, possibleMatch)) {
481                                 pango_font_description_free(bestMatchForNewDescr);
482                                 bestMatchForNewDescr = possibleMatch;
483                                 bestMatchFontDescription = currentFontSpec;
484                             } else {
485                                 pango_font_description_free(possibleMatch);
486                             }
487                         }
488                     }
489                 }
491                 newFontSpec = bestMatchFontDescription;
493                 pango_font_description_free(newFontDescr);
494                 pango_font_description_free(bestMatchForNewDescr);
495             }
496         }
498         pango_font_description_free(descr);
499     }
501     return newFontSpec;
504 /**
505     apply style property to the given font
506     @param fontSpec the given font
507     @param turnOn true to set italic style
508     @return the changed fontspec, if the property can not be set return an empty string
509 */
510 Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn)
512     Glib::ustring newFontSpec;
514     // Find the PangoFontDesecription that goes with this font specification string
515     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
517     if (it != fontInstanceMap.end()) {
518         // If we did find one, make a copy and set/unset the italic as needed
519         PangoFontDescription *descr = pango_font_description_copy((*it).second);
521         PangoStyle style;
522         if (turnOn) {
523             style = PANGO_STYLE_ITALIC;
524         } else {
525             style = PANGO_STYLE_NORMAL;
526         }
527         pango_font_description_set_style(descr, style);
529         newFontSpec = ConstructFontSpecification(descr);
530         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
531             if(turnOn) {
532                 // there is no PANGO_STYLE_ITALIC let's test for PANGO_STYLE_OBLIQUE
533                 style = PANGO_STYLE_OBLIQUE;
534                 pango_font_description_set_style(descr, style);
536                 newFontSpec = ConstructFontSpecification(descr);
537                 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
538                     // If the new font does not have even an oblique face, don't
539                     // allow italics to be set!
540                     newFontSpec = Glib::ustring("");
541                 }
543             } else {
544                 // If the new font does not have an italic face, don't
545                 // allow italics to be set!
546                 newFontSpec = Glib::ustring("");
547             }
548         }
550         pango_font_description_free(descr);
551     }
553     return newFontSpec;
556 /**
557     apply width property to the given font
558     @param fontSpec the given font
559     @param turnOn true to set bold
560     @return the changed fontspec, if the property can not be set return an empty string
561 */
562 Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn)
564     Glib::ustring newFontSpec;
566     // Find the PangoFontDesecription that goes with this font specification string
567     PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
569     if (it != fontInstanceMap.end()) {
570         // If we did find one, make a copy and set/unset the bold as needed
571         PangoFontDescription *descr = pango_font_description_copy((*it).second);
573         PangoWeight weight;
574         if (turnOn) {
575             weight = PANGO_WEIGHT_BOLD;
576         } else {
577             weight = PANGO_WEIGHT_NORMAL;
578         }
579         pango_font_description_set_weight(descr, weight);
581         newFontSpec = ConstructFontSpecification(descr);
582         if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
583             // If the new font does not have a bold face, don't
584             // allow bold to be set!
585             newFontSpec = Glib::ustring("");
586         }
588         pango_font_description_free(descr);
589     }
591     return newFontSpec;
594 /////
596 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
598     return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
601 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
603     g_assert(map);
605     if (map) {
607         // Gather the family names as listed by Pango
608         PangoFontFamily**  families = NULL;
609         int numFamilies = 0;
610         pango_font_map_list_families(fontServer, &families, &numFamilies);
612         for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) {
614             // Gather the styles for this family
615             PangoFontFace** faces = NULL;
616             int numFaces = 0;
617             pango_font_family_list_faces(families[currentFamily], &faces, &numFaces);
619             for (int currentFace=0; currentFace < numFaces; currentFace++) {
621                 // If the face has a name, describe it, and then use the
622                 // description to get the UI family and face strings
624                 if (pango_font_face_get_face_name(faces[currentFace]) == NULL) {
625                     continue;
626                 }
628                 PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
629                 if (faceDescr) {
630                     Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
631                     Glib::ustring styleUIName = GetUIStyleString(faceDescr);
633                     if (!familyUIName.empty() && !styleUIName.empty()) {
634                         // Find the right place to put the style information, adding
635                         // a map entry for the family name if it doesn't yet exist
637                         FamilyToStylesMap::iterator iter = map->find(familyUIName);
639                         if (iter == map->end()) {
640                             map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>()));
641                         }
643                         // Insert into the style list and save the info in the reference maps
644                         // only if the style does not yet exist
646                         bool exists = false;
647                         std::list<Glib::ustring> &styleList = (*map)[familyUIName];
649                         for (std::list<Glib::ustring>::iterator it=styleList.begin();
650                                  it != styleList.end();
651                                  it++) {
652                             if (*it == styleUIName) {
653                                 exists = true;
654                                 break;
655                             }
656                         }
658                         if (!exists) {
659                             styleList.push_back(styleUIName);
661                             // Add the string info needed in the reference maps
662                             fontStringMap.insert(
663                                     std::make_pair(
664                                             Glib::ustring(familyUIName) + Glib::ustring(styleUIName),
665                                             ConstructFontSpecification(faceDescr)));
666                             fontInstanceMap.insert(
667                                     std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
668                         } else {
669                             pango_font_description_free(faceDescr);
670                         }
671                     } else {
672                         pango_font_description_free(faceDescr);
673                     }
674                 }
675             }
676         }
678         // Sort the style lists
679         for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
680             (*iter).second.sort(StyleNameCompareInternal);
681         }
682     }
685 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
687     font_instance *font = NULL;
689     g_assert(style);
691     if (style) {
692         //  First try to use the font specification if it is set
693         if (style->text->font_specification.set
694             && style->text->font_specification.value
695             && *style->text->font_specification.value) {
697             font = FaceFromFontSpecification(style->text->font_specification.value);
698         }
700         // If that failed, try using the CSS information in the style
701         if (!font) {
702             font = Face(style->text->font_family.value, font_style_to_pos(*style));
703         }
704     }
706     return font;
709 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
711     PangoFontDescription *temp_descr = pango_font_description_from_string(style);
712     pango_font_description_set_family(temp_descr,family);
713     font_instance *res = Face(temp_descr);
714     pango_font_description_free(temp_descr);
715     return res;
718 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
720     font_instance *fontInstance = NULL;
722     g_assert(uiFamily && uiStyle);
723     if (uiFamily && uiStyle) {
724         Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle);
726         UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString);
728         if (uiToPangoIter != fontStringMap.end ()) {
729             PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second);
730             if (pangoToDescrIter != fontInstanceMap.end()) {
731                 // We found the pango description - now we can make a font_instance
732                 PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second);
733                 fontInstance = Face(tempDescr);
734                 pango_font_description_free(tempDescr);
735             }
736         }
737     }
739     return fontInstance;
742 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
744     font_instance *fontInstance = NULL;
746     g_assert(pangoString);
748     if (pangoString) {
749         PangoFontDescription *descr = NULL;
751         // First attempt to find the font specification in the reference map
752         PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString));
753         if (it != fontInstanceMap.end()) {
754             descr = pango_font_description_copy((*it).second);
755         }
757         // Or create a font description from the string - this may fail or
758         // produce unexpected results if the string does not have a good format
759         if (!descr) {
760             descr = pango_font_description_from_string(pangoString);
761         }
763         if (descr && (pango_font_description_get_family(descr) != NULL)) {
764             fontInstance = Face(descr);
765         }
767         if (descr) {
768             pango_font_description_free(descr);
769         }
770     }
772     return fontInstance;
775 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
777     font_instance *font = NULL;
779     g_assert(fontSpecification);
781     if (fontSpecification) {
782         // How the string is used to reconstruct a font depends on how it
783         // was constructed in ConstructFontSpecification.  As it stands,
784         // the font specification is a pango-created string
785         font = FaceFromPangoString(fontSpecification);
786     }
788     return font;
793 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
795 #ifdef USE_PANGO_WIN32
796     // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
797     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
798 #else
799     pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
800 #endif
802     font_instance *res = NULL;
804     FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
805     if ( loadedFaces.find(descr) == loadedFaces.end() ) {
806         // not yet loaded
807         PangoFont *nFace = NULL;
809         // workaround for bug #1025565.
810         // fonts without families blow up Pango.
811         if (pango_font_description_get_family(descr) != NULL) {
812             nFace = pango_font_map_load_font(fontServer,fontContext,descr);
813         }
814         else {
815             g_warning(_("Ignoring font without family that will crash Pango"));
816         }
818         if ( nFace ) {
819             // duplicate FcPattern, the hard way
820             res = new font_instance();
821             // store the descr of the font we asked for, since this is the key where we intend
822             // to put the font_instance at in the unordered_map.  the descr of the returned
823             // pangofont may differ from what was asked, so we don't know (at this
824             // point) whether loadedFaces[that_descr] is free or not (and overwriting
825             // an entry will bring deallocation problems)
826             res->descr = pango_font_description_copy(descr);
827             res->daddy = this;
828             res->InstallFace(nFace);
829             if ( res->pFont == NULL ) {
830                 // failed to install face -> bitmap font
831                 // printf("face failed\n");
832                 res->daddy = NULL;
833                 delete res;
834                 res = NULL;
835                 if ( canFail ) {
836                     char *tc = pango_font_description_to_string(descr);
837                     PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
838                     g_free(tc);
839                     pango_font_description_set_family(descr,"Sans");
840                     res = Face(descr,false);
841                 }
842             } else {
843                 loadedFaces[res->descr]=res;
844                 res->Ref();
845                 AddInCache(res);
846             }
847         } else {
848             // no match
849             if ( canFail ) {
850                 PANGO_DEBUG("falling back to Sans\n");
851                 descr = pango_font_description_new();
852                 pango_font_description_set_family(descr,"Sans");
853                 res = Face(descr,false);
854                 pango_font_description_free(descr);
855             }
856         }
857     } else {
858         // already here
859         res = loadedFaces[descr];
860         res->Ref();
861         AddInCache(res);
862     }
863     if (res) {
864         res->InitTheFace();
865     }
866     return res;
869 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
871     PangoFontDescription *temp_descr = pango_font_description_new();
872     pango_font_description_set_family(temp_descr,family);
873     pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
874     pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
875     pango_font_description_set_style(temp_descr,(PangoStyle)style);
876     pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
877     font_instance *res = Face(temp_descr);
878     pango_font_description_free(temp_descr);
879     return res;
882 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
884     PangoFontDescription *temp_descr = pango_font_description_new();
886     pango_font_description_set_family(temp_descr, family);
888     if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
889         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
890     } else {
891         pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
892     }
894     if ( apos.italic ) {
895         pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
896     } else if ( apos.oblique ) {
897         pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
898     } else {
899         pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
900     }
902     if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
903         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
904     } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
905         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
906     } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
907         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
908     } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
909         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
910     } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
911         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
912     } else {
913         pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
914     }
916     if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
917         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
918     } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
919         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
920     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
921         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
922     } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
923         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
924     } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
925         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
926     } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
927         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
928     } else {
929         pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
930     }
932     font_instance *res = Face(temp_descr);
933     pango_font_description_free(temp_descr);
934     return res;
937 void font_factory::UnrefFace(font_instance *who)
939     if ( who ) {
940         FaceMapType& loadedFaces = *static_cast<FaceMapType*>(loadedPtr);
942         if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
943             // not found
944             char *tc = pango_font_description_to_string(who->descr);
945             g_warning("unrefFace %p=%s: failed\n",who,tc);
946             g_free(tc);
947         } else {
948             loadedFaces.erase(loadedFaces.find(who->descr));
949             //                  printf("unrefFace %p: success\n",who);
950         }
951     }
954 void font_factory::AddInCache(font_instance *who)
956     if ( who == NULL ) return;
957     for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
958     for (int i = 0;i < nbEnt;i++) {
959         if ( ents[i].f == who ) {
960             //                  printf("present\n");
961             ents[i].age += 1.0;
962             return;
963         }
964     }
965     if ( nbEnt > maxEnt ) {
966         printf("cache sur-plein?\n");
967         return;
968     }
969     who->Ref();
970     if ( nbEnt == maxEnt ) {
971         int    bi = 0;
972         double ba = ents[bi].age;
973         for (int i = 1;i < nbEnt;i++) {
974             if ( ents[i].age < ba ) {
975                 bi = i;
976                 ba = ents[bi].age;
977             }
978         }
979         ents[bi].f->Unref();
980         ents[bi]=ents[--nbEnt];
981     }
982     ents[nbEnt].f = who;
983     ents[nbEnt].age = 1.0;
984     nbEnt++;
988 /*
989   Local Variables:
990   mode:c++
991   c-file-style:"stroustrup"
992   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
993   indent-tabs-mode:nil
994   fill-column:99
995   End:
996 */
997 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :