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