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>
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include <glibmm/i18n.h> // _()
21 /* Freetype2 */
22 # include <pango/pangoft2.h>
25 // need to avoid using the size field
26 size_t font_descr_hash::operator()( PangoFontDescription *const &x) const {
27 int h = 0;
28 h *= 1128467;
29 char const *theF = pango_font_description_get_family(x);
30 h += (theF)?g_str_hash(theF):0;
31 h *= 1128467;
32 h += (int)pango_font_description_get_style(x);
33 h *= 1128467;
34 h += (int)pango_font_description_get_variant(x);
35 h *= 1128467;
36 h += (int)pango_font_description_get_weight(x);
37 h *= 1128467;
38 h += (int)pango_font_description_get_stretch(x);
39 return h;
40 }
41 bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) {
42 //if ( pango_font_description_equal(a,b) ) return true;
43 char const *fa = pango_font_description_get_family(a);
44 char const *fb = pango_font_description_get_family(b);
45 if ( ( fa && fb == NULL ) || ( fb && fa == NULL ) ) return false;
46 if ( fa && fb && strcmp(fa,fb) != 0 ) return false;
47 if ( pango_font_description_get_style(a) != pango_font_description_get_style(b) ) return false;
48 if ( pango_font_description_get_variant(a) != pango_font_description_get_variant(b) ) return false;
49 if ( pango_font_description_get_weight(a) != pango_font_description_get_weight(b) ) return false;
50 if ( pango_font_description_get_stretch(a) != pango_font_description_get_stretch(b) ) return false;
51 return true;
52 }
54 /////////////////// helper functions
56 /**
57 * A wrapper for strcasestr that also provides an implementation for Win32.
58 */
59 static bool
60 ink_strstr(char const *haystack, char const *pneedle)
61 {
62 // windows has no strcasestr implementation, so here is ours...
63 // stolen from nmap
64 /* FIXME: This is broken for e.g. ink_strstr("aab", "ab"). Report to nmap.
65 *
66 * Also, suggest use of g_ascii_todown instead of buffer stuff, and g_ascii_tolower instead
67 * of tolower. Given that haystack is a font name (i.e. fairly short), it should be ok to
68 * do g_ascii_strdown on both haystack and pneedle, and do normal strstr.
69 *
70 * Rather than fixing in inkscape, consider getting rid of this routine, instead using
71 * strdown and plain strstr at caller. We have control over the needle values, so we can
72 * modify the callers rather than calling strdown there.
73 */
74 char buf[512];
75 register char const *p;
76 char *needle, *q, *foundto;
77 if (!*pneedle) return true;
78 if (!haystack) return false;
80 needle = buf;
81 p = pneedle; q = needle;
82 while ((*q++ = tolower(*p++)))
83 ;
84 p = haystack - 1; foundto = needle;
85 while (*++p) {
86 if (tolower(*p) == *foundto) {
87 if (!*++foundto) {
88 /* Yeah, we found it */
89 return true;
90 }
91 } else foundto = needle;
92 }
93 return false;
94 }
96 /**
97 * Regular fonts are 'Regular', 'Roman', 'Normal', or 'Plain'
98 */
99 // FIXME: make this UTF8, add non-English style names
100 static bool
101 is_regular(char const *s)
102 {
103 if (ink_strstr(s, "Regular")) return true;
104 if (ink_strstr(s, "Roman")) return true;
105 if (ink_strstr(s, "Normal")) return true;
106 if (ink_strstr(s, "Plain")) return true;
107 return false;
108 }
110 /**
111 * Non-bold fonts are 'Medium' or 'Book'
112 */
113 static bool
114 is_nonbold(char const *s)
115 {
116 if (ink_strstr(s, "Medium")) return true;
117 if (ink_strstr(s, "Book")) return true;
118 return false;
119 }
121 /**
122 * Italic fonts are 'Italic', 'Oblique', or 'Slanted'
123 */
124 static bool
125 is_italic(char const *s)
126 {
127 if (ink_strstr(s, "Italic")) return true;
128 if (ink_strstr(s, "Oblique")) return true;
129 if (ink_strstr(s, "Slanted")) return true;
130 return false;
131 }
133 /**
134 * Bold fonts are 'Bold'
135 */
136 static bool
137 is_bold(char const *s)
138 {
139 if (ink_strstr(s, "Bold")) return true;
140 return false;
141 }
143 /**
144 * Caps fonts are 'Caps'
145 */
146 static bool
147 is_caps(char const *s)
148 {
149 if (ink_strstr(s, "Caps")) return true;
150 return false;
151 }
153 #if 0 /* FIXME: These are all unused. Please delete them or use them (presumably in
154 * style_name_compare). */
155 /**
156 * Monospaced fonts are 'Mono'
157 */
158 static bool
159 is_mono(char const *s)
160 {
161 if (ink_strstr(s, "Mono")) return true;
162 return false;
163 }
165 /**
166 * Rounded fonts are 'Round'
167 */
168 static bool
169 is_round(char const *s)
170 {
171 if (ink_strstr(s, "Round")) return true;
172 return false;
173 }
175 /**
176 * Outline fonts are 'Outline'
177 */
178 static bool
179 is_outline(char const *s)
180 {
181 if (ink_strstr(s, "Outline")) return true;
182 return false;
183 }
185 /**
186 * Swash fonts are 'Swash'
187 */
188 static bool
189 is_swash(char const *s)
190 {
191 if (ink_strstr(s, "Swash")) return true;
192 return false;
193 }
194 #endif
196 /**
197 * Determines if two style names match. This allows us to match
198 * based on the type of style rather than simply doing string matching,
199 * because for instance 'Plain' and 'Normal' mean the same thing.
200 *
201 * Q: Shouldn't this include the other tests such as is_outline, etc.?
202 * Q: Is there a problem with strcasecmp on Win32? Should it use stricmp?
203 */
204 static int
205 style_name_compare(void const *aa, void const *bb)
206 {
207 char const *a = (char const *) aa;
208 char const *b = (char const *) bb;
210 if (is_regular(a) && !is_regular(b)) return -1;
211 if (is_regular(b) && !is_regular(a)) return 1;
213 if (is_bold(a) && !is_bold(b)) return 1;
214 if (is_bold(b) && !is_bold(a)) return -1;
216 if (is_italic(a) && !is_italic(b)) return 1;
217 if (is_italic(b) && !is_italic(a)) return -1;
219 if (is_nonbold(a) && !is_nonbold(b)) return 1;
220 if (is_nonbold(b) && !is_nonbold(a)) return -1;
222 if (is_caps(a) && !is_caps(b)) return 1;
223 if (is_caps(b) && !is_caps(a)) return -1;
225 return strcasecmp(a, b);
226 }
228 static int
229 style_record_compare(void const *aa, void const *bb)
230 {
231 NRStyleRecord const *a = (NRStyleRecord const *) aa;
232 NRStyleRecord const *b = (NRStyleRecord const *) bb;
234 return (style_name_compare(a->name, b->name));
235 }
237 static void font_factory_name_list_destructor(NRNameList *list)
238 {
239 for (unsigned int i = 0; i < list->length; i++)
240 free(list->names[i]);
241 if ( list->names ) nr_free(list->names);
242 }
244 static void font_factory_style_list_destructor(NRStyleList *list)
245 {
246 for (unsigned int i = 0; i < list->length; i++) {
247 free((void *) (list->records)[i].name);
248 free((void *) (list->records)[i].descr);
249 }
250 if ( list->records ) nr_free(list->records);
251 }
253 /**
254 * On Win32 performs a stricmp(a,b), otherwise does a strcasecmp(a,b)
255 */
256 static int
257 family_name_compare(void const *a, void const *b)
258 {
259 #ifndef WIN32
260 return strcasecmp((*((char const **) a)), (*((char const **) b)));
261 #else
262 return stricmp((*((char const **) a)), (*((char const **) b)));
263 #endif
264 }
266 void noop(...) {}
267 //#define PANGO_DEBUG g_print
268 #define PANGO_DEBUG noop
272 ///////////////////// FontFactory
273 #ifndef USE_PANGO_WIN32
274 // the substitute function to tell fontconfig to enforce outline fonts
275 void FactorySubstituteFunc(FcPattern *pattern,gpointer /*data*/)
276 {
277 FcPatternAddBool(pattern, "FC_OUTLINE",FcTrue);
278 //char *fam = NULL;
279 //FcPatternGetString(pattern, "FC_FAMILY",0, &fam);
280 //printf("subst_f on %s\n",fam);
281 }
282 #endif
285 font_factory *font_factory::lUsine = NULL;
287 font_factory *font_factory::Default(void)
288 {
289 if ( lUsine == NULL ) lUsine = new font_factory;
290 return lUsine;
291 }
293 font_factory::font_factory(void)
294 {
295 fontSize = 512;
296 nbEnt = 0;
297 maxEnt = 32;
298 ents = (font_entry*)malloc(maxEnt*sizeof(font_entry));
300 #ifdef USE_PANGO_WIN32
301 hScreenDC = pango_win32_get_dc();
302 fontServer = pango_win32_font_map_for_display();
303 fontContext = pango_win32_get_context();
304 pangoFontCache = pango_win32_font_map_get_font_cache(fontServer);
305 #else
306 fontServer = pango_ft2_font_map_new();
307 pango_ft2_font_map_set_resolution((PangoFT2FontMap*)fontServer, 72, 72);
308 fontContext = pango_ft2_font_map_create_context((PangoFT2FontMap*)fontServer);
309 pango_ft2_font_map_set_default_substitute((PangoFT2FontMap*)fontServer,FactorySubstituteFunc,this,NULL);
310 #endif
311 }
313 font_factory::~font_factory(void)
314 {
315 for (int i = 0;i < nbEnt;i++) ents[i].f->Unref();
316 if ( ents ) free(ents);
318 g_object_unref(fontServer);
319 #ifdef USE_PANGO_WIN32
320 pango_win32_shutdown_display();
321 #else
322 //pango_ft2_shutdown_display();
323 #endif
324 //g_object_unref(fontContext);
325 }
327 font_instance *font_factory::FaceFromDescr(char const *family, char const *style)
328 {
329 PangoFontDescription *temp_descr = pango_font_description_from_string(style);
330 pango_font_description_set_family(temp_descr,family);
331 font_instance *res = Face(temp_descr);
332 pango_font_description_free(temp_descr);
333 return res;
334 }
336 font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail)
337 {
338 #ifdef USE_PANGO_WIN32
339 // damn Pango fudges the size, so we need to unfudge. See source of pango_win32_font_map_init()
340 pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE*72/GetDeviceCaps(pango_win32_get_dc(),LOGPIXELSY))); // mandatory huge size (hinting workaround)
341 #else
342 pango_font_description_set_size(descr, (int) (fontSize*PANGO_SCALE)); // mandatory huge size (hinting workaround)
343 #endif
345 font_instance *res = NULL;
347 if ( loadedFaces.find(descr) == loadedFaces.end() ) {
348 // not yet loaded
349 PangoFont *nFace = NULL;
351 // workaround for bug #1025565.
352 // fonts without families blow up Pango.
353 if (pango_font_description_get_family(descr) != NULL) {
354 nFace = pango_font_map_load_font(fontServer,fontContext,descr);
355 }
356 else {
357 g_warning(_("Ignoring font without family that will crash Pango"));
358 }
360 if ( nFace ) {
361 // duplicate FcPattern, the hard way
362 res = new font_instance();
363 // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at
364 // in the hash_map. the descr of the returned pangofont may differ from what was asked, so we don't know (at this
365 // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems)
366 res->descr = pango_font_description_copy(descr);
367 res->daddy = this;
368 res->InstallFace(nFace);
369 if ( res->pFont == NULL ) {
370 // failed to install face -> bitmap font
371 // printf("face failed\n");
372 res->daddy = NULL;
373 delete res;
374 res = NULL;
375 if ( canFail ) {
376 char *tc = pango_font_description_to_string(descr);
377 PANGO_DEBUG("falling back from %s to Sans because InstallFace failed\n",tc);
378 free(tc);
379 pango_font_description_set_family(descr,"Sans");
380 res = Face(descr,false);
381 }
382 } else {
383 loadedFaces[res->descr]=res;
384 res->Ref();
385 AddInCache(res);
386 }
387 } else {
388 // no match
389 if ( canFail ) {
390 PANGO_DEBUG("falling back to Sans\n");
391 pango_font_description_set_family(descr,"Sans");
392 res = Face(descr,false);
393 }
394 }
395 } else {
396 // already here
397 res = loadedFaces[descr];
398 res->Ref();
399 AddInCache(res);
400 }
401 res->InitTheFace();
402 return res;
403 }
405 font_instance *font_factory::Face(char const *family, int variant, int style, int weight, int stretch, int /*size*/, int /*spacing*/)
406 {
407 PangoFontDescription *temp_descr = pango_font_description_new();
408 pango_font_description_set_family(temp_descr,family);
409 pango_font_description_set_weight(temp_descr,(PangoWeight)weight);
410 pango_font_description_set_stretch(temp_descr,(PangoStretch)stretch);
411 pango_font_description_set_style(temp_descr,(PangoStyle)style);
412 pango_font_description_set_variant(temp_descr,(PangoVariant)variant);
413 font_instance *res = Face(temp_descr);
414 pango_font_description_free(temp_descr);
415 return res;
416 }
418 font_instance *font_factory::Face(char const *family, NRTypePosDef apos)
419 {
420 PangoFontDescription *temp_descr = pango_font_description_new();
422 pango_font_description_set_family(temp_descr, family);
424 if ( apos.variant == NR_POS_VARIANT_SMALLCAPS ) {
425 pango_font_description_set_variant(temp_descr, PANGO_VARIANT_SMALL_CAPS);
426 } else {
427 pango_font_description_set_variant(temp_descr, PANGO_VARIANT_NORMAL);
428 }
430 if ( apos.italic ) {
431 pango_font_description_set_style(temp_descr, PANGO_STYLE_ITALIC);
432 } else if ( apos.oblique ) {
433 pango_font_description_set_style(temp_descr, PANGO_STYLE_OBLIQUE);
434 } else {
435 pango_font_description_set_style(temp_descr, PANGO_STYLE_NORMAL);
436 }
438 if ( apos.weight <= NR_POS_WEIGHT_ULTRA_LIGHT ) {
439 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRALIGHT);
440 } else if ( apos.weight <= NR_POS_WEIGHT_LIGHT ) {
441 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_LIGHT);
442 } else if ( apos.weight <= NR_POS_WEIGHT_NORMAL ) {
443 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_NORMAL);
444 } else if ( apos.weight <= NR_POS_WEIGHT_BOLD ) {
445 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_BOLD);
446 } else if ( apos.weight <= NR_POS_WEIGHT_ULTRA_BOLD ) {
447 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_ULTRABOLD);
448 } else {
449 pango_font_description_set_weight(temp_descr, PANGO_WEIGHT_HEAVY);
450 }
452 if ( apos.stretch <= NR_POS_STRETCH_ULTRA_CONDENSED ) {
453 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_CONDENSED);
454 } else if ( apos.stretch <= NR_POS_STRETCH_CONDENSED ) {
455 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_CONDENSED);
456 } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_CONDENSED ) {
457 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_CONDENSED);
458 } else if ( apos.stretch <= NR_POS_WEIGHT_NORMAL ) {
459 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_NORMAL);
460 } else if ( apos.stretch <= NR_POS_STRETCH_SEMI_EXPANDED ) {
461 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_SEMI_EXPANDED);
462 } else if ( apos.stretch <= NR_POS_STRETCH_EXPANDED ) {
463 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXPANDED);
464 } else {
465 pango_font_description_set_stretch(temp_descr, PANGO_STRETCH_EXTRA_EXPANDED);
466 }
468 font_instance *res = Face(temp_descr);
469 pango_font_description_free(temp_descr);
470 return res;
471 }
473 void font_factory::UnrefFace(font_instance *who)
474 {
475 if ( who == NULL ) return;
476 if ( loadedFaces.find(who->descr) == loadedFaces.end() ) {
477 // not found
478 char *tc = pango_font_description_to_string(who->descr);
479 g_warning("unrefFace %p=%s: failed\n",who,tc);
480 free(tc);
481 } else {
482 loadedFaces.erase(loadedFaces.find(who->descr));
483 // printf("unrefFace %p: success\n",who);
484 }
485 }
487 NRNameList *font_factory::Families(NRNameList *flist)
488 {
489 PangoFontFamily** fams = NULL;
490 int nbFam = 0;
491 pango_font_map_list_families(fontServer, &fams, &nbFam);
493 PANGO_DEBUG("got %d families\n", nbFam);
495 flist->length = nbFam;
496 flist->names = (guchar **)malloc(nbFam*sizeof(guchar*));
497 flist->destructor = font_factory_name_list_destructor;
499 for (int i = 0;i < nbFam;i++) {
500 // Note: on Windows, pango_font_family_get_name always returns lowercase name.
501 // As a result the list of fonts in the dialog is lowercase.
502 // We could work around by loading the font and taking pango_font_description_get_family from its descr (that gives correct case),
503 // but this is slow, and it's better to fix Pango instead.
504 flist->names[i]=(guchar*)strdup(pango_font_family_get_name(fams[i]));
505 }
507 qsort(flist->names, nbFam, sizeof(guchar *), family_name_compare);
509 g_free(fams);
511 return flist;
512 }
514 NRStyleList *font_factory::Styles(gchar const *family, NRStyleList *slist)
515 {
516 PangoFontFamily *theFam = NULL;
518 // search available families
519 {
520 PangoFontFamily** fams = NULL;
521 int nbFam = 0;
522 pango_font_map_list_families(fontServer, &fams, &nbFam);
524 for (int i = 0;i < nbFam;i++) {
525 char const *fname = pango_font_family_get_name(fams[i]);
526 if ( fname && strcmp(family,fname) == 0 ) {
527 theFam = fams[i];
528 break;
529 }
530 }
532 g_free(fams);
533 }
535 // nothing found
536 if ( theFam == NULL ) {
537 slist->length = 0;
538 slist->records = NULL;
539 slist->destructor = NULL;
540 return slist;
541 }
543 // search faces in the found family
544 PangoFontFace** faces = NULL;
545 int nFaces = 0;
546 pango_font_family_list_faces(theFam, &faces, &nFaces);
548 slist->records = (NRStyleRecord *) malloc(nFaces * sizeof(NRStyleRecord));
549 slist->destructor = font_factory_style_list_destructor;
551 int nr = 0;
552 for (int i = 0; i < nFaces; i++) {
554 // no unnamed faces
555 if (pango_font_face_get_face_name(faces[i]) == NULL)
556 continue;
557 PangoFontDescription *nd = pango_font_face_describe(faces[i]);
558 if (nd == NULL)
559 continue;
560 char const *descr = pango_font_description_to_string(nd);
561 if (descr == NULL) {
562 pango_font_description_free(nd);
563 continue;
564 }
566 char const *name = g_strdup(pango_font_face_get_face_name(faces[i]));
567 pango_font_description_free(nd);
569 slist->records[nr].name = name;
570 slist->records[nr].descr = descr;
571 nr ++;
572 }
574 slist->length = nr;
576 qsort(slist->records, slist->length, sizeof(NRStyleRecord), style_record_compare);
577 /* effic: Consider doing strdown and all the is_italic etc. tests once off and store the
578 * results in a table, rather than having the sort invoke multiple is_italic tests per
579 * record.
580 */
582 g_free(faces);
584 return slist;
585 }
587 void font_factory::AddInCache(font_instance *who)
588 {
589 if ( who == NULL ) return;
590 for (int i = 0;i < nbEnt;i++) ents[i].age *= 0.9;
591 for (int i = 0;i < nbEnt;i++) {
592 if ( ents[i].f == who ) {
593 // printf("present\n");
594 ents[i].age += 1.0;
595 return;
596 }
597 }
598 if ( nbEnt > maxEnt ) {
599 printf("cache sur-plein?\n");
600 return;
601 }
602 who->Ref();
603 if ( nbEnt == maxEnt ) {
604 int bi = 0;
605 double ba = ents[bi].age;
606 for (int i = 1;i < nbEnt;i++) {
607 if ( ents[i].age < ba ) {
608 bi = i;
609 ba = ents[bi].age;
610 }
611 }
612 ents[bi].f->Unref();
613 ents[bi]=ents[--nbEnt];
614 }
615 ents[nbEnt].f = who;
616 ents[nbEnt].age = 1.0;
617 nbEnt++;
618 }
621 /*
622 Local Variables:
623 mode:c++
624 c-file-style:"stroustrup"
625 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
626 indent-tabs-mode:nil
627 fill-column:99
628 End:
629 */
630 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :