1 /*
2 * FontFactory.cpp
3 * testICU
4 *
5 * Authors:
6 * fred
7 * bulia byak <buliabyak@users.sf.net>
8 *
9 */
11 #include "FontFactory.h"
12 #include <libnrtype/font-instance.h>
14 #include <glibmm.h>
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <glib/gmem.h>
22 #include <glibmm/i18n.h> // _()
24 /* Freetype2 */
25 # include <pango/pangoft2.h>
28 // need to avoid using the size field
29 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
30 int h = 0;
31 h *= 1128467;
32 char const *theF = pango_font_description_get_family(x);
33 h += (theF)?g_str_hash(theF):0;
34 h *= 1128467;
35 h += (int)pango_font_description_get_style(x);
36 h *= 1128467;
37 h += (int)pango_font_description_get_variant(x);
38 h *= 1128467;
39 h += (int)pango_font_description_get_weight(x);
40 h *= 1128467;
41 h += (int)pango_font_description_get_stretch(x);
42 return h;
43 }
44 bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) {
45 //if ( pango_font_description_equal(a,b) ) return true;
46 char const *fa = pango_font_description_get_family(a);
47 char const *fb = pango_font_description_get_family(b);
48 if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
49 if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
50 if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
51 if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
52 if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
53 if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
54 return true;
55 }
57 /////////////////// helper functions
59 /**
60 * A wrapper for strcasestr that also provides an implementation for Win32.
61 */
62 static bool
63 ink_strstr(char const *haystack, char const *pneedle)
64 {
65 // windows has no strcasestr implementation, so here is ours...
66 // stolen from nmap
67 /* FIXME: This is broken for e.g. ink_strstr("aab", "ab"). Report to nmap.
68 *
69 * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
70 * of tolower. Given that haystack is a font name (i.e. fairly short), it should be ok to
71 * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
72 *
73 * Rather than fixing in inkscape, consider getting rid of this routine, instead using
74 * strdown and plain strstr at caller. We have control over the needle values, so we can
75 * modify the callers rather than calling strdown there.
76 */
77 char buf[512];
78 register char const *p;
79 char *needle, *q, *foundto;
80 if (!*pneedle) return true;
81 if (!haystack) return false;
83 needle = buf;
84 p = pneedle; q = needle;
85 while ((*q++ = tolower(*p++)))
86 ;
87 p = haystack - 1; foundto = needle;
88 while (*++p) {
89 if (tolower(*p) == *foundto) {
90 if (!*++foundto) {
91 /* Yeah, we found it */
92 return true;
93 }
94 } else foundto = needle;
95 }
96 return false;
97 }
99 /**
100 * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
101 */
102 // FIXME: make this UTF8, add non-English style names
103 static bool
104 is_regular(char const *s)
105 {
106 if (ink_strstr(s, "Regular")) return true;
107 if (ink_strstr(s, "Roman")) return true;
108 if (ink_strstr(s, "Normal")) return true;
109 if (ink_strstr(s, "Plain")) return true;
110 return false;
111 }
113 /**
114 * Non-bold fonts are 'Medium' or 'Book'
115 */
116 static bool
117 is_nonbold(char const *s)
118 {
119 if (ink_strstr(s, "Medium")) return true;
120 if (ink_strstr(s, "Book")) return true;
121 return false;
122 }
124 /**
125 * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
126 */
127 static bool
128 is_italic(char const *s)
129 {
130 if (ink_strstr(s, "Italic")) return true;
131 if (ink_strstr(s, "Oblique")) return true;
132 if (ink_strstr(s, "Slanted")) return true;
133 return false;
134 }
136 /**
137 * Bold fonts are 'Bold'
138 */
139 static bool
140 is_bold(char const *s)
141 {
142 if (ink_strstr(s, "Bold")) return true;
143 return false;
144 }
146 /**
147 * Caps fonts are 'Caps'
148 */
149 static bool
150 is_caps(char const *s)
151 {
152 if (ink_strstr(s, "Caps")) return true;
153 return false;
154 }
156 #if 0 /* FIXME: These are all unused. Please delete them or use them (presumably in
157 * style_name_compare). */
158 /**
159 * Monospaced fonts are 'Mono'
160 */
161 static bool
162 is_mono(char const *s)
163 {
164 if (ink_strstr(s, "Mono")) return true;
165 return false;
166 }
168 /**
169 * Rounded fonts are 'Round'
170 */
171 static bool
172 is_round(char const *s)
173 {
174 if (ink_strstr(s, "Round")) return true;
175 return false;
176 }
178 /**
179 * Outline fonts are 'Outline'
180 */
181 static bool
182 is_outline(char const *s)
183 {
184 if (ink_strstr(s, "Outline")) return true;
185 return false;
186 }
188 /**
189 * Swash fonts are 'Swash'
190 */
191 static bool
192 is_swash(char const *s)
193 {
194 if (ink_strstr(s, "Swash")) return true;
195 return false;
196 }
197 #endif
199 /**
200 * Determines if two style names match. This allows us to match
201 * based on the type of style rather than simply doing string matching,
202 * because for instance 'Plain' and 'Normal' mean the same thing.
203 *
204 * Q: Shouldn't this include the other tests such as is_outline, etc.?
205 * Q: Is there a problem with strcasecmp on Win32? Should it use stricmp?
206 */
207 int
208 style_name_compare(char const *aa, char const *bb)
209 {
210 char const *a = (char const *) aa;
211 char const *b = (char const *) bb;
213 if (is_regular(a) && !is_regular(b)) return -1;
214 if (is_regular(b) && !is_regular(a)) return 1;
216 if (is_bold(a) && !is_bold(b)) return 1;
217 if (is_bold(b) && !is_bold(a)) return -1;
219 if (is_italic(a) && !is_italic(b)) return 1;
220 if (is_italic(b) && !is_italic(a)) return -1;
222 if (is_nonbold(a) && !is_nonbold(b)) return 1;
223 if (is_nonbold(b) && !is_nonbold(a)) return -1;
225 if (is_caps(a) && !is_caps(b)) return 1;
226 if (is_caps(b) && !is_caps(a)) return -1;
228 return strcasecmp(a, b);
229 }
231 /*
232 defined but not used:
234 static int
235 style_record_compare(void const *aa, void const *bb)
236 {
237 NRStyleRecord const *a = (NRStyleRecord const *) aa;
238 NRStyleRecord const *b = (NRStyleRecord const *) bb;
240 return (style_name_compare(a->name, b->name));
241 }
243 static void font_factory_name_list_destructor(NRNameList *list)
244 {
245 for (unsigned int i = 0; i < list->length; i++)
246 g_free(list->names[i]);
247 if ( list->names ) g_free(list->names);
248 }
250 static void font_factory_style_list_destructor(NRStyleList *list)
251 {
252 for (unsigned int i = 0; i < list->length; i++) {
253 g_free((void *) (list->records)[i].name);
254 g_free((void *) (list->records)[i].descr);
255 }
256 if ( list->records ) g_free(list->records);
257 }
258 */
260 /**
261 * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
262 */
263 int
264 family_name_compare(char const *a, char const *b)
265 {
266 #ifndef WIN32
267 return strcasecmp((*((char const **) a)), (*((char const **) b)));
268 #else
269 return stricmp((*((char const **) a)), (*((char const **) b)));
270 #endif
271 }
273 void noop(...) {}
274 //#define PANGO_DEBUG g_print
275 #define PANGO_DEBUG noop
279 ///////////////////// FontFactory
280 #ifndef USE_PANGO_WIN32
281 // the substitute function to tell fontconfig to enforce outline fonts
282 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
283 {
284 FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
285 //char *fam = NULL;
286 //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
287 //printf("subst_f on %s\n",fam);
288 }
289 #endif
292 font_factory *font_factory::lUsine = NULL;
294 font_factory *font_factory::Default(void)
295 {
296 if ( lUsine == NULL ) lUsine = new font_factory;
297 return lUsine;
298 }
300 font_factory::font_factory(void)
301 {
302 fontSize = 512;
303 nbEnt = 0;
304 maxEnt = 32;
305 ents = (font_entry*)g_malloc(maxEnt*sizeof(font_entry));
307 #ifdef USE_PANGO_WIN32
308 hScreenDC = pango_win32_get_dc();
309 fontServer = pango_win32_font_map_for_display();
310 fontContext = pango_win32_get_context();
311 pangoFontCache = pango_win32_font_map_get_font_cache(fontServer);
312 #else
313 fontServer = pango_ft2_font_map_new();
314 pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
315 fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
316 pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
317 #endif
318 }
320 font_factory::~font_factory(void)
321 {
322 for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
323 if ( ents ) g_free(ents);
325 g_object_unref(fontServer);
326 #ifdef USE_PANGO_WIN32
327 pango_win32_shutdown_display();
328 #else
329 //pango_ft2_shutdown_display();
330 #endif
331 //g_object_unref(fontContext);
333 // Delete the pango font pointers in the string to instance map
334 PangoStringToDescrMap::iterator it = fontInstanceMap.begin();
335 while (it != fontInstanceMap.end()) {
336 pango_font_description_free((*it).second);
337 it++;
338 }
339 }
342 Glib::ustring font_factory::ConstructFontSpecification(PangoFontDescription *font)
343 {
344 Glib::ustring pangoString;
346 g_assert(font);
348 if (font) {
349 // Once the format for the font specification is decided, it must be
350 // kept.. if it is absolutely necessary to change it, the attribute
351 // it is written to needs to have a new version so the legacy files
352 // can be read.
354 PangoFontDescription *copy = pango_font_description_copy(font);
356 pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE);
357 pangoString = Glib::ustring(pango_font_description_to_string(copy));
359 pango_font_description_free(copy);
361 }
363 return pangoString;
364 }
366 Glib::ustring font_factory::ConstructFontSpecification(font_instance *font)
367 {
368 Glib::ustring pangoString;
370 g_assert(font);
372 if (font) {
373 pangoString = ConstructFontSpecification(font->descr);
374 }
376 return pangoString;
377 }
379 Glib::ustring font_factory::GetUIFamilyString(PangoFontDescription const *fontDescr)
380 {
381 Glib::ustring family;
383 g_assert(fontDescr);
385 if (fontDescr) {
386 // For now, keep it as family name taken from pango
387 family = pango_font_description_get_family(fontDescr);
388 }
390 return family;
391 }
393 Glib::ustring font_factory::GetUIStyleString(PangoFontDescription const *fontDescr)
394 {
395 Glib::ustring style;
397 g_assert(fontDescr);
399 if (fontDescr) {
400 PangoFontDescription *fontDescrCopy = pango_font_description_copy(fontDescr);
402 pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_FAMILY);
403 pango_font_description_unset_fields(fontDescrCopy, PANGO_FONT_MASK_SIZE);
405 // For now, keep it as style name taken from pango
406 style = pango_font_description_to_string(fontDescrCopy);
408 pango_font_description_free(fontDescrCopy);
409 }
411 return style;
412 }
414 Glib::ustring font_factory::ReplaceFontSpecificationFamily(const Glib::ustring & fontSpec, const Glib::ustring & newFamily)
415 {
416 Glib::ustring newFontSpec;
418 // Although we are using the string from pango_font_description_to_string for the
419 // font specification, we definitely cannot just set the new family in the
420 // PangoFontDescription structure and ask for a new string. This is because
421 // what constitutes a "family" in our own UI may be different from how Pango
422 // sees it.
424 // Find the PangoFontDescription associated to this fontSpec
425 PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
427 if (it != fontInstanceMap.end()) {
428 PangoFontDescription *descr = pango_font_description_copy((*it).second);
430 // Grab the UI Family string from the descr
431 Glib::ustring uiFamily = GetUIFamilyString(descr);
433 // Replace the UI Family name with the new family name
434 std::size_t found = fontSpec.find(uiFamily);
435 if (found != Glib::ustring::npos) {
436 newFontSpec = fontSpec;
437 newFontSpec.erase(found, uiFamily.size());
438 newFontSpec.insert(found, newFamily);
440 // If the new font specification does not exist in the reference maps,
441 // search for the next best match for the faces in that style
442 it = fontInstanceMap.find(newFontSpec);
443 if (it == fontInstanceMap.end()) {
445 PangoFontDescription *newFontDescr = pango_font_description_from_string(newFontSpec.c_str());
447 PangoFontDescription *bestMatchForNewDescr = NULL;
448 Glib::ustring bestMatchFontDescription;
450 bool setFirstFamilyMatch = false;
451 for (it = fontInstanceMap.begin(); it != fontInstanceMap.end(); it++) {
453 Glib::ustring currentFontSpec = (*it).first;
455 // Save some time by only looking at the right family
456 if (currentFontSpec.find(newFamily) != Glib::ustring::npos) {
457 if (!setFirstFamilyMatch) {
458 // This ensures that the closest match is at least within the correct
459 // family rather than the first font in the list
460 bestMatchForNewDescr = pango_font_description_copy((*it).second);
461 bestMatchFontDescription = currentFontSpec;
462 setFirstFamilyMatch = true;
463 } else {
464 // Get the font description that corresponds, and
465 // then see if we've found a better match
466 PangoFontDescription *possibleMatch = pango_font_description_copy((*it).second);
468 if (pango_font_description_better_match(
469 newFontDescr, bestMatchForNewDescr, possibleMatch)) {
471 pango_font_description_free(bestMatchForNewDescr);
472 bestMatchForNewDescr = possibleMatch;
473 bestMatchFontDescription = currentFontSpec;
474 } else {
475 pango_font_description_free(possibleMatch);
476 }
477 }
478 }
479 }
481 newFontSpec = bestMatchFontDescription;
483 pango_font_description_free(newFontDescr);
484 pango_font_description_free(bestMatchForNewDescr);
485 }
486 }
488 pango_font_description_free(descr);
489 }
491 return newFontSpec;
492 }
494 Glib::ustring font_factory::FontSpecificationSetItalic(const Glib::ustring & fontSpec, bool turnOn)
495 {
496 Glib::ustring newFontSpec;
498 // Find the PangoFontDesecription that goes with this font specification string
499 PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
501 if (it != fontInstanceMap.end()) {
502 // If we did find one, make a copy and set/unset the italic as needed
503 PangoFontDescription *descr = pango_font_description_copy((*it).second);
505 PangoStyle style;
506 if (turnOn) {
507 style = PANGO_STYLE_ITALIC;
508 } else {
509 style = PANGO_STYLE_NORMAL;
510 }
511 pango_font_description_set_style(descr, style);
513 newFontSpec = ConstructFontSpecification(descr);
514 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
515 // If the new font does not have an italic face, don't
516 // allow italics to be set!
517 newFontSpec = fontSpec;
518 }
520 pango_font_description_free(descr);
521 }
523 return newFontSpec;
524 }
526 Glib::ustring font_factory::FontSpecificationSetBold(const Glib::ustring & fontSpec, bool turnOn)
527 {
528 Glib::ustring newFontSpec;
530 // Find the PangoFontDesecription that goes with this font specification string
531 PangoStringToDescrMap::iterator it = fontInstanceMap.find(fontSpec);
533 if (it != fontInstanceMap.end()) {
534 // If we did find one, make a copy and set/unset the bold as needed
535 PangoFontDescription *descr = pango_font_description_copy((*it).second);
537 PangoWeight weight;
538 if (turnOn) {
539 weight = PANGO_WEIGHT_BOLD;
540 } else {
541 weight = PANGO_WEIGHT_NORMAL;
542 }
543 pango_font_description_set_weight(descr, weight);
545 newFontSpec = ConstructFontSpecification(descr);
546 if (fontInstanceMap.find(newFontSpec) == fontInstanceMap.end()) {
547 // If the new font does not have a bold face, don't
548 // allow bold to be set!
549 newFontSpec = fontSpec;
550 }
552 pango_font_description_free(descr);
553 }
555 return newFontSpec;
556 }
558 /////
560 static bool StyleNameCompareInternal(Glib::ustring style1, Glib::ustring style2)
561 {
562 return (style_name_compare(style1.c_str(), style2.c_str()) < 0);
563 }
565 void font_factory::GetUIFamiliesAndStyles(FamilyToStylesMap *map)
566 {
567 g_assert(map);
569 if (map) {
571 // Gather the family names as listed by Pango
572 PangoFontFamily** families = NULL;
573 int numFamilies = 0;
574 pango_font_map_list_families(fontServer, &families, &numFamilies);
576 for (int currentFamily=0; currentFamily < numFamilies; currentFamily++) {
578 // Gather the styles for this family
579 PangoFontFace** faces = NULL;
580 int numFaces = 0;
581 pango_font_family_list_faces(families[currentFamily], &faces, &numFaces);
583 for (int currentFace=0; currentFace < numFaces; currentFace++) {
585 // If the face has a name, describe it, and then use the
586 // description to get the UI family and face strings
588 if (pango_font_face_get_face_name(faces[currentFace]) == NULL) {
589 continue;
590 }
592 PangoFontDescription *faceDescr = pango_font_face_describe(faces[currentFace]);
593 if (faceDescr) {
594 Glib::ustring familyUIName = GetUIFamilyString(faceDescr);
595 Glib::ustring styleUIName = GetUIStyleString(faceDescr);
597 if (!familyUIName.empty() && !styleUIName.empty()) {
598 // Find the right place to put the style information, adding
599 // a map entry for the family name if it doesn't yet exist
601 FamilyToStylesMap::iterator iter = map->find(familyUIName);
603 if (iter == map->end()) {
604 map->insert(std::make_pair(familyUIName, std::list<Glib::ustring>()));
605 }
607 // Insert into the style list and save the info in the reference maps
608 // only if the style does not yet exist
610 bool exists = false;
611 std::list<Glib::ustring> &styleList = (*map)[familyUIName];
613 for (std::list<Glib::ustring>::iterator it=styleList.begin();
614 it != styleList.end();
615 it++) {
616 if (*it == styleUIName) {
617 exists = true;
618 break;
619 }
620 }
622 if (!exists) {
623 styleList.push_back(styleUIName);
625 // Add the string info needed in the reference maps
626 fontStringMap.insert(
627 std::make_pair(
628 Glib::ustring(familyUIName) + Glib::ustring(styleUIName),
629 ConstructFontSpecification(faceDescr)));
630 fontInstanceMap.insert(
631 std::make_pair(ConstructFontSpecification(faceDescr), faceDescr));
632 } else {
633 pango_font_description_free(faceDescr);
634 }
635 } else {
636 pango_font_description_free(faceDescr);
637 }
638 }
639 }
640 }
642 // Sort the style lists
643 for (FamilyToStylesMap::iterator iter = map->begin() ; iter != map->end(); iter++) {
644 (*iter).second.sort(StyleNameCompareInternal);
645 }
646 }
647 }
649 font_instance* font_factory::FaceFromStyle(SPStyle const *style)
650 {
651 font_instance *font = NULL;
653 g_assert(style);
655 if (style) {
656 // First try to use the font specification if it is set
657 if (style->text->font_specification.set
658 && style->text->font_specification.value
659 && *style->text->font_specification.value) {
661 font = FaceFromFontSpecification(style->text->font_specification.value);
662 }
664 // If that failed, try using the CSS information in the style
665 if (!font) {
666 font = Face(style->text->font_family.value, font_style_to_pos(*style));
667 }
668 }
670 return font;
671 }
673 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
674 {
675 PangoFontDescription *temp_descr = pango_font_description_from_string(style);
676 pango_font_description_set_family(temp_descr,family);
677 font_instance *res = Face(temp_descr);
678 pango_font_description_free(temp_descr);
679 return res;
680 }
682 font_instance* font_factory::FaceFromUIStrings(char const *uiFamily, char const *uiStyle)
683 {
684 font_instance *fontInstance = NULL;
686 g_assert(uiFamily && uiStyle);
687 if (uiFamily && uiStyle) {
688 Glib::ustring uiString = Glib::ustring(uiFamily) + Glib::ustring(uiStyle);
690 UIStringToPangoStringMap::iterator uiToPangoIter = fontStringMap.find(uiString);
692 if (uiToPangoIter != fontStringMap.end ()) {
693 PangoStringToDescrMap::iterator pangoToDescrIter = fontInstanceMap.find((*uiToPangoIter).second);
694 if (pangoToDescrIter != fontInstanceMap.end()) {
695 // We found the pango description - now we can make a font_instance
696 PangoFontDescription *tempDescr = pango_font_description_copy((*pangoToDescrIter).second);
697 fontInstance = Face(tempDescr);
698 pango_font_description_free(tempDescr);
699 }
700 }
701 }
703 return fontInstance;
704 }
706 font_instance* font_factory::FaceFromPangoString(char const *pangoString)
707 {
708 font_instance *fontInstance = NULL;
710 g_assert(pangoString);
712 if (pangoString) {
713 PangoFontDescription *descr = NULL;
715 // First attempt to find the font specification in the reference map
716 PangoStringToDescrMap::iterator it = fontInstanceMap.find(Glib::ustring(pangoString));
717 if (it != fontInstanceMap.end()) {
718 descr = pango_font_description_copy((*it).second);
719 }
721 // Or create a font description from the string - this may fail or
722 // produce unexpected results if the string does not have a good format
723 if (!descr) {
724 descr = pango_font_description_from_string(pangoString);
725 }
727 if (descr && (pango_font_description_get_family(descr) != NULL)) {
728 fontInstance = Face(descr);
729 }
731 if (descr) {
732 pango_font_description_free(descr);
733 }
734 }
736 return fontInstance;
737 }
739 font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecification)
740 {
741 font_instance *font = NULL;
743 g_assert(fontSpecification);
745 if (fontSpecification) {
746 // How the string is used to reconstruct a font depends on how it
747 // was constructed in ConstructFontSpecification. As it stands,
748 // the font specification is a pango-created string
749 font = FaceFromPangoString(fontSpecification);
750 }
752 return font;
753 }
757 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
758 {
759 #ifdef USE_PANGO_WIN32
760 // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
761 pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
762 #else
763 pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
764 #endif
766 font_instance *res = NULL;
768 if ( loadedFaces.find(descr) == loadedFaces.end() ) {
769 // not yet loaded
770 PangoFont *nFace = NULL;
772 // workaround for bug #1025565.
773 // fonts without families blow up Pango.
774 if (pango_font_description_get_family(descr) != NULL) {
775 nFace = pango_font_map_load_font(fontServer,fontContext,descr);
776 }
777 else {
778 g_warning(_("Ignoring font without family that will crash Pango"));
779 }
781 if ( nFace ) {
782 // duplicate FcPattern, the hard way
783 res = new font_instance();
784 // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at
785 // in the hash_map. the descr of the returned pangofont may differ from what was asked, so we don't know (at this
786 // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems)
787 res->descr = pango_font_description_copy(descr);
788 res->daddy = this;
789 res->InstallFace(nFace);
790 if ( res->pFont == NULL ) {
791 // failed to install face -> bitmap font
792 // printf("face failed\n");
793 res->daddy = NULL;
794 delete res;
795 res = NULL;
796 if ( canFail ) {
797 char *tc = pango_font_description_to_string(descr);
798 PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
799 g_free(tc);
800 pango_font_description_set_family(descr,"Sans");
801 res = Face(descr,false);
802 }
803 } else {
804 loadedFaces[res->descr]=res;
805 res->Ref();
806 AddInCache(res);
807 }
808 } else {
809 // no match
810 if ( canFail ) {
811 PANGO_DEBUG("falling back to Sans\n");
812 descr = pango_font_description_new();
813 pango_font_description_set_family(descr,"Sans");
814 res = Face(descr,false);
815 pango_font_description_free(descr);
816 }
817 }
818 } else {
819 // already here
820 res = loadedFaces[descr];
821 res->Ref();
822 AddInCache(res);
823 }
824 res->InitTheFace();
825 return res;
826 }
828 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
829 {
830 PangoFontDescription *temp_descr = pango_font_description_new();
831 pango_font_description_set_family(temp_descr,family);
832 pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
833 pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
834 pango_font_description_set_style(temp_descr,(PangoStyle)style);
835 pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
836 font_instance *res = Face(temp_descr);
837 pango_font_description_free(temp_descr);
838 return res;
839 }
841 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
842 {
843 PangoFontDescription *temp_descr = pango_font_description_new();
845 pango_font_description_set_family(temp_descr, family);
847 if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
848 pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
849 } else {
850 pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
851 }
853 if ( apos.italic ) {
854 pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
855 } else if ( apos.oblique ) {
856 pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
857 } else {
858 pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
859 }
861 if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
862 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
863 } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
864 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
865 } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
866 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
867 } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
868 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
869 } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
870 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
871 } else {
872 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
873 }
875 if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
876 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
877 } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
878 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
879 } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
880 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
881 } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
882 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
883 } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
884 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
885 } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
886 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
887 } else {
888 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
889 }
891 font_instance *res = Face(temp_descr);
892 pango_font_description_free(temp_descr);
893 return res;
894 }
896 void font_factory::UnrefFace(font_instance *who)
897 {
898 if ( who == NULL ) return;
899 if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
900 // not found
901 char *tc = pango_font_description_to_string(who->descr);
902 g_warning("unrefFace %p=%s: failed\n",who,tc);
903 g_free(tc);
904 } else {
905 loadedFaces.erase(loadedFaces.find(who->descr));
906 // printf("unrefFace %p: success\n",who);
907 }
908 }
910 void font_factory::AddInCache(font_instance *who)
911 {
912 if ( who == NULL ) return;
913 for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
914 for (int i = 0;i < nbEnt;i++) {
915 if ( ents[i].f == who ) {
916 // printf("present\n");
917 ents[i].age += 1.0;
918 return;
919 }
920 }
921 if ( nbEnt > maxEnt ) {
922 printf("cache sur-plein?\n");
923 return;
924 }
925 who->Ref();
926 if ( nbEnt == maxEnt ) {
927 int bi = 0;
928 double ba = ents[bi].age;
929 for (int i = 1;i < nbEnt;i++) {
930 if ( ents[i].age < ba ) {
931 bi = i;
932 ba = ents[bi].age;
933 }
934 }
935 ents[bi].f->Unref();
936 ents[bi]=ents[--nbEnt];
937 }
938 ents[nbEnt].f = who;
939 ents[nbEnt].age = 1.0;
940 nbEnt++;
941 }
944 /*
945 Local Variables:
946 mode:c++
947 c-file-style:"stroustrup"
948 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
949 indent-tabs-mode:nil
950 fill-column:99
951 End:
952 */
953 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :