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