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;
103 }
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)
111 {
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;
117 }
119 /**
120 * Non-bold fonts are 'Medium' or 'Book'
121 */
122 static bool
123 is_nonbold(char const *s)
124 {
125 if (ink_strstr(s, "Medium")) return true;
126 if (ink_strstr(s, "Book")) return true;
127 return false;
128 }
130 /**
131 * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
132 */
133 static bool
134 is_italic(char const *s)
135 {
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;
140 }
142 /**
143 * Bold fonts are 'Bold'
144 */
145 static bool
146 is_bold(char const *s)
147 {
148 if (ink_strstr(s, "Bold")) return true;
149 return false;
150 }
152 /**
153 * Caps fonts are 'Caps'
154 */
155 static bool
156 is_caps(char const *s)
157 {
158 if (ink_strstr(s, "Caps")) return true;
159 return false;
160 }
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)
169 {
170 if (ink_strstr(s, "Mono")) return true;
171 return false;
172 }
174 /**
175 * Rounded fonts are 'Round'
176 */
177 static bool
178 is_round(char const *s)
179 {
180 if (ink_strstr(s, "Round")) return true;
181 return false;
182 }
184 /**
185 * Outline fonts are 'Outline'
186 */
187 static bool
188 is_outline(char const *s)
189 {
190 if (ink_strstr(s, "Outline")) return true;
191 return false;
192 }
194 /**
195 * Swash fonts are 'Swash'
196 */
197 static bool
198 is_swash(char const *s)
199 {
200 if (ink_strstr(s, "Swash")) return true;
201 return false;
202 }
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)
215 {
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);
235 }
237 /*
238 defined but not used:
240 static int
241 style_record_compare(void const *aa, void const *bb)
242 {
243 NRStyleRecord const *a = (NRStyleRecord const *) aa;
244 NRStyleRecord const *b = (NRStyleRecord const *) bb;
246 return (style_name_compare(a->name, b->name));
247 }
249 static void font_factory_name_list_destructor(NRNameList *list)
250 {
251 for (unsigned int i = 0; i < list->length; i++)
252 g_free(list->names[i]);
253 if ( list->names ) g_free(list->names);
254 }
256 static void font_factory_style_list_destructor(NRStyleList *list)
257 {
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);
263 }
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)
271 {
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
277 }
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*/)
289 {
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);
294 }
295 #endif
298 font_factory *font_factory::lUsine = NULL;
300 font_factory *font_factory::Default(void)
301 {
302 if ( lUsine == NULL ) lUsine = new font_factory;
303 return lUsine;
304 }
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())
322 {
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
329 }
331 font_factory::~font_factory(void)
332 {
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 }
356 }
359 Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font)
360 {
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;
381 }
383 Glib::ustring font_factory::ConstructFontSpecification(font_instance *font)
384 {
385 Glib::ustring pangoString;
387 g_assert(font);
389 if (font) {
390 pangoString = ConstructFontSpecification(font->descr);
391 }
393 return pangoString;
394 }
396 Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr)
397 {
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;
408 }
410 Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr)
411 {
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;
429 }
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)
440 {
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;
484 }
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)
496 {
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.
545 }
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)
556 {
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.
589 }
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 )
601 {
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;
664 }
666 /////
668 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
669 {
670 return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
671 }
673 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
674 {
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 }
755 }
757 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
758 {
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;
789 }
791 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
792 {
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;
798 }
800 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
801 {
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;
822 }
824 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
825 {
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;
855 }
857 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
858 {
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;
871 }
875 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
876 {
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;
949 }
951 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
952 {
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;
962 }
964 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
965 {
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;
1026 }
1028 void font_factory::UnrefFace(font_instance *who)
1029 {
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 }
1043 }
1045 void font_factory::AddInCache(font_instance *who)
1046 {
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++;
1076 }
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 :