1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <pthread.h>
29 
30 #ifdef WITH_XMB
31 #include <wchar.h>
32 #endif
33 
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 
37 #include "_xitk.h"
38 #include "recode.h"
39 
40 #define XITK_CACHE_SIZE 10
41 #define XITK_FONT_LENGTH_NAME 256
42 
43 typedef struct {
44   unsigned int             lru;        /* old of item for LRU */
45   xitk_font_t             *font;
46 } xitk_font_cache_item_t;
47 
48 typedef struct {
49   xitk_dnode_t             node;
50   xitk_font_t             *font;       /* remembered loaded font */
51   unsigned int             number;     /* number of instances of this font */
52 } xitk_font_list_item_t;
53 
54 struct xitk_font_cache_s {
55   size_t                   n;
56   size_t                   nlist;      /* number of fonts in the cache and in the list */
57 
58   unsigned int             life;       /* life counter for LRU */
59   xitk_dlist_t             loaded;     /* list of loaded fonts */
60   pthread_mutex_t          mutex;      /* protecting mutex */
61   xitk_font_cache_item_t   items[XITK_CACHE_SIZE]; /* cache */
62 
63   xitk_recode_t           *xr;         /* text recoding */
64 };
65 
66 #if defined(WITH_XMB) && !defined(WITH_XFT)
67 /*
68  * Guess if error occured, release missing charsets list.
69  * There is a problem with rendering when "ISO10646-1" charset is missing.
70  */
xitk_font_guess_error(XFontSet fs,char * name,char ** missing,int count)71 static int xitk_font_guess_error(XFontSet fs, char *name, char **missing, int count) {
72   int i;
73 
74   if(fs == NULL)
75     return 1;
76 
77   if(count) {
78     /* some uncovered codepages */
79     for(i = 0; i < count; i++) {
80       if(strcasecmp("ISO10646-1", missing[i]) == 0) {
81         XITK_WARNING("font \"%s\" doesn't contain charset %s\n", name, missing[i]);
82         XFreeStringList(missing);
83         return 1;
84       }
85     }
86 
87     XFreeStringList(missing);
88   }
89 
90   return 0;
91 }
92 #endif
93 
94 #ifdef WITH_XFT
95 /* convert a -*-* .. style font description into something Xft can digest */
xitk_font_core_string_to_xft(char * new_name,size_t new_name_size,const char * old_name)96 static const char * xitk_font_core_string_to_xft(char *new_name, size_t new_name_size, const char * old_name) {
97 
98   if(old_name[0] == '-' || old_name[0] == '*') {
99     char  family[50], weight[15], slant[8], pxsize[5], ptsize[5];
100 
101     if( old_name[0] == '-' )
102       old_name++;
103 
104     /* Extract description elements */
105     family[0] = weight[0] = slant[0] = pxsize[0] = ptsize[0] = '\0';
106     sscanf(old_name, "%*[^-]-%49[^-]", family);
107     sscanf(old_name, "%*[^-]-%*[^-]-%14[^-]", weight);
108     sscanf(old_name, "%*[^-]-%*[^-]-%*[^-]-%7[^-]", slant);
109     sscanf(old_name, "%*[^-]-%*[^-]-%*[^-]-%*[^-]-%*[^-]-%*[^-]-%4[^-]", pxsize);
110     sscanf(old_name, "%*[^-]-%*[^-]-%*[^-]-%*[^-]-%*[^-]-%*[^-]-%*[^-]-%4[^-]", ptsize);
111 
112     /* Transform the elements */
113     if(!strcmp(weight, "*"))
114       weight[0] = '\0';
115     if(!strcmp(pxsize, "*"))
116       pxsize[0] = '\0';
117     if(!strcmp(ptsize, "*"))
118       ptsize[0] = '\0';
119     strcpy(slant,
120 	   (!strcmp(slant, "i")) ? "italic" :
121 	   (!strcmp(slant, "o")) ? "oblique" :
122 	   (!strcmp(slant, "r")) ? "roman" :
123 				   "");
124 
125     /* Xft doesn't have lucida, which is a small font;
126      * thus we make whatever is chosen 2 sizes smaller */
127     if(!strcasecmp(family, "lucida")) {
128       if(pxsize[0]) {
129 	int sz = strtol(pxsize, NULL, 10) - 2;
130 	snprintf(pxsize, sizeof(pxsize), "%i", sz);
131       }
132       if(ptsize[0]) {
133 	int sz = strtol(ptsize, NULL, 10) - 2;
134 	snprintf(ptsize, sizeof(ptsize), "%i", sz);
135       }
136     }
137 
138     /* Build Xft font description string from the elements */
139     snprintf(new_name, new_name_size, "%s%s%s%s%s%s%s%s%s",
140 	     family,
141 	     ((weight[0]) ? ":weight="    : ""), weight,
142 	     ((slant[0])  ? ":slant="     : ""), slant,
143 	     ((pxsize[0]) ? ":pixelsize=" : ""), pxsize,
144 	     ((ptsize[0]) ? ":size="      : ""), ptsize);
145 
146     return new_name;
147   }
148 
149   return old_name;
150 }
151 #endif
152 
153 #if defined(WITH_XMB) && !defined(WITH_XFT)
154 /*
155  * XCreateFontSet requires font name starting with '-'
156  */
xitk_font_right_name(const char * name)157 static char *xitk_font_right_name(const char *name) {
158   char *right_name;
159 
160   ABORT_IF_NULL(name);
161 
162   right_name = strdup(name);
163 
164   switch(right_name[0]) {
165   case '-':
166     break;
167   case '*':
168     right_name[0] = '-';
169     break;
170   default:
171     right_name[0] = '*';
172     break;
173   }
174 
175   return right_name;
176 }
177 #endif
178 
179 /*
180  * load the font and returns status
181  */
xitk_font_load_one(Display * display,const char * font,xitk_font_t * xtfs)182 static int xitk_font_load_one(Display *display, const char *font, xitk_font_t *xtfs) {
183   int    ok;
184 #ifdef WITH_XFT
185 #else
186 # ifdef WITH_XMB
187   char **missing;
188   char  *def;
189   char  *right_name;
190   int    count;
191 
192   if(xitk_get_xmb_enability()) {
193     right_name = xitk_font_right_name(font);
194     XLOCK (xitk_x_lock_display, display);
195     xtfs->fontset = XCreateFontSet(display, right_name, &missing, &count, &def);
196     ok = !xitk_font_guess_error(xtfs->fontset, right_name, missing, count);
197     XUNLOCK (xitk_x_unlock_display, display);
198     free(right_name);
199   }
200   else
201 # endif
202 #endif
203   {
204     char new_name[255];
205     XLOCK (xitk_x_lock_display, display);
206 #ifdef WITH_XFT
207     xtfs->font = XftFontOpenName( display, DefaultScreen(display),
208                                   xitk_font_core_string_to_xft(new_name, sizeof(new_name), font));
209 #else
210     xtfs->font = XLoadQueryFont(display, font);
211 #endif
212     XUNLOCK (xitk_x_unlock_display, display);
213     ok = (xtfs->font != NULL);
214   }
215 
216   if(ok)
217     xtfs->display = display;
218 
219   return ok;
220 }
221 
222 /*
223  * unload the font
224  */
xitk_font_unload_one(xitk_font_t * xtfs)225 static void xitk_font_unload_one(xitk_font_t *xtfs) {
226   XLOCK (xitk_x_lock_display, xtfs->display);
227 #ifndef WITH_XFT
228 #ifdef WITH_XMB
229   if(xitk_get_xmb_enability())
230     XFreeFontSet(xtfs->display, xtfs->fontset);
231   else
232 #endif
233     XFreeFont(xtfs->display, xtfs->font);
234 #else
235     XftFontClose( xtfs->display, xtfs->font );
236 #endif
237   XUNLOCK (xitk_x_unlock_display, xtfs->display);
238   free(xtfs->name);
239 }
240 
241 /*
242  * init font cache subsystem
243  */
xitk_font_cache_init(void)244 void xitk_font_cache_init (void) {
245   xitk_t *xitk = gXitk;
246 
247   xitk->font_cache = malloc (sizeof (*xitk->font_cache));
248 
249   if (xitk->font_cache) {
250     xitk->font_cache->n    = 0;
251     xitk->font_cache->life = 0;
252     xitk_dlist_init (&xitk->font_cache->loaded);
253     pthread_mutex_init (&xitk->font_cache->mutex, NULL);
254 #ifdef WITH_XFT
255     xitk->font_cache->xr = xitk_recode_init (NULL, "UTF-8", 1);
256 #else
257     xitk->font_cache->xr = NULL;
258 #endif
259   }
260 }
261 
262 /*
263  * destroy font cache subsystem
264  */
xitk_font_cache_done(void)265 void xitk_font_cache_done(void) {
266   xitk_t *xitk = gXitk;
267   size_t       i;
268   xitk_font_t *xtfs;
269 
270   for (i = 0; i < xitk->font_cache->n; i++) {
271     xtfs = xitk->font_cache->items[i].font;
272 
273     xitk_font_unload_one(xtfs);
274 
275     free(xtfs);
276   }
277   xitk->font_cache->n = 0;
278 
279 #ifdef XITK_DEBUG
280   {
281     xitk_font_list_item_t *item = (xitk_font_list_item_t *)cached.loaded.head.next;
282     if (item->node.next) {
283       XITK_WARNING ("%s(): list of loaded font isn't empty:\n", __FUNCTION__);
284       do {
285         printf ("  %s (%u)\n", item->font->name, item->number);
286         item = (xitk_font_list_item_t *)item->node.next;
287       } while (item->node.next);
288     }
289   }
290 #endif
291 
292   xitk_dlist_clear (&xitk->font_cache->loaded);
293   pthread_mutex_destroy(&xitk->font_cache->mutex);
294 
295   xitk_recode_done(xitk->font_cache->xr);
296 
297   free (xitk->font_cache);
298   xitk->font_cache = NULL;
299 }
300 
301 /*
302  * get index of oldest item in the cache
303  */
xitk_cache_get_oldest(void)304 static size_t xitk_cache_get_oldest(void) {
305   xitk_t *xitk = gXitk;
306   size_t          i, oldest;
307   unsigned long   lru;
308 
309   if(!xitk->font_cache->n) {
310     fprintf(stderr, "%s(%d): cache.n == 0. Aborting.\n", __FUNCTION__, __LINE__);
311     abort();
312   }
313 
314   i      = 0;
315   oldest = 0;
316   lru    = xitk->font_cache->items[0].lru;
317 
318   for(i = 1; i < xitk->font_cache->n; i++) {
319     if(xitk->font_cache->items[i].lru < lru) {
320       oldest = i;
321       lru = xitk->font_cache->items[i].lru;
322     }
323   }
324 
325   return oldest;
326 }
327 
328 /*
329  * place item into cache and adjust all counters for LRU
330  */
xitk_cache_insert_final(size_t pos,xitk_font_t * font)331 static void xitk_cache_insert_final(size_t pos, xitk_font_t *font) {
332   xitk_t *xitk = gXitk;
333   size_t i;
334 
335   xitk->font_cache->items[pos].lru  = xitk->font_cache->life;
336   xitk->font_cache->items[pos].font = font;
337 
338   if(xitk->font_cache->life + 1 == 0) {
339     xitk->font_cache->life >>= 1;
340     for(i = 0; i < xitk->font_cache->n; i++)
341       xitk->font_cache->items[i].lru >>= 1;
342   }
343   else
344     xitk->font_cache->life++;
345 }
346 
347 /*
348  * insert item into sorted cache, it shifts part of cache to move gap
349  */
xitk_cache_insert_into(size_t gap,int step,xitk_font_t * font)350 static void xitk_cache_insert_into(size_t gap, int step, xitk_font_t *font) {
351   xitk_t *xitk = gXitk;
352   size_t i;
353 
354   i = gap;
355   while (((step == -1 && i > 0) || (step == 1 && i < xitk->font_cache->n - 1))
356 	 && strcmp(font->name, xitk->font_cache->items[i + step].font->name) * step > 0) {
357     xitk->font_cache->items[i] = xitk->font_cache->items[i + step];
358     i += step;
359   }
360   xitk_cache_insert_final(i, font);
361 }
362 
363 /*
364  * add new item into cache, if it's full the oldest item will be released
365  */
xitk_cache_add_item(xitk_font_t * font)366 static void xitk_cache_add_item(xitk_font_t *font) {
367   xitk_t *xitk = gXitk;
368   size_t       oldest;
369   int          cmp;
370   xitk_font_t *xtfs;
371 
372   if(xitk->font_cache->n < XITK_CACHE_SIZE) {
373     /* cache is not full */
374     xitk->font_cache->n++;
375     xitk_cache_insert_into(xitk->font_cache->n - 1, -1, font);
376   }
377   else {
378     /* cache is full */
379     /* free the oldest item */
380     oldest = xitk_cache_get_oldest();
381     cmp    = strcmp(font->name, xitk->font_cache->items[oldest].font->name);
382     xtfs   = xitk->font_cache->items[oldest].font;
383 
384 #ifdef LOG
385     printf("xiTK: dropped \"%s\", list: %zu, cache: %zu\n", xtfs->name, xitk->font_cache->nlist, xitk->font_cache->n);
386 #endif
387 
388     xitk_font_unload_one(xtfs);
389 
390     free(xtfs);
391 
392     /* insert new item into sorted cache */
393     if(cmp == 0)
394       xitk_cache_insert_final(oldest, font);
395     else {
396       if(cmp < 0)
397 	cmp = -1;
398       else
399 	cmp = 1;
400 
401       xitk_cache_insert_into(oldest, cmp, font);
402     }
403   }
404 }
405 
406 /*
407  * remove the item in 'pos' and returns its data
408  */
xitk_cache_remove_item(size_t pos)409 static xitk_font_t *xitk_cache_remove_item(size_t pos) {
410   xitk_t *xitk = gXitk;
411   xitk_font_t *xtfs;
412   size_t       i;
413 
414   xtfs = xitk->font_cache->items[pos].font;
415   for (i = pos; i < xitk->font_cache->n - 1; i++)
416     xitk->font_cache->items[i] = xitk->font_cache->items[i + 1];
417   xitk->font_cache->n--;
418 
419   return xtfs;
420 }
421 
422 /*
423  * search the font of given display in the cache, remove it from the cache
424  */
xitk_cache_take_item(Display * display,const char * name)425 static xitk_font_t *xitk_cache_take_item(Display *display, const char *name) {
426   xitk_t *xitk = gXitk;
427   int     left, right;
428   size_t  i, j;
429   int     cmp;
430 
431   if(!xitk->font_cache->n)
432     return NULL;
433 
434   /* binary search in the cache */
435   left  = 0;
436   right = xitk->font_cache->n - 1;
437 
438   while(right >= left) {
439     i = (left + right) >> 1;
440     cmp = strcmp(name, xitk->font_cache->items[i].font->name);
441 
442     if(cmp < 0)
443       right = i - 1;
444     else if (cmp > 0)
445       left = i + 1;
446     else {
447       /* found right name, search right display */
448       /* forward */
449       j = i;
450       while(1) {
451 	if(xitk->font_cache->items[j].font->display == display)
452 	  return xitk_cache_remove_item(j);
453 
454 	if(!j)
455 	  break;
456 
457 	cmp = strcmp(name, xitk->font_cache->items[--j].font->name);
458       }
459       /* backward */
460       j = i + 1;
461 
462       while(j < xitk->font_cache->n) {
463 	cmp = strcmp(name, xitk->font_cache->items[j].font->name);
464 
465 	if(cmp != 0)
466 	  return NULL;
467 
468 	if(xitk->font_cache->items[j].font->display == display)
469 	  return xitk_cache_remove_item(j);
470 
471 	j++;
472       }
473 
474       /* not in given display */
475       return NULL;
476     }
477   }
478 
479   /* no such name */
480   return NULL;
481 }
482 
483 /*
484  * search the font in the list of loaded fonts
485  */
cache_get_from_list(Display * display,const char * name)486 static xitk_font_list_item_t *cache_get_from_list(Display *display, const char *name) {
487   xitk_t *xitk = gXitk;
488   xitk_font_list_item_t *item = (xitk_font_list_item_t *)xitk->font_cache->loaded.head.next;
489   while (item->node.next) {
490     if ((strcmp(name, item->font->name) == 0) && (display == item->font->display))
491       return item;
492     item = (xitk_font_list_item_t *)item->node.next;
493   }
494   return NULL;
495 }
496 
497 /*
498  *
499  */
xitk_font_load_font(Display * display,const char * font)500 xitk_font_t *xitk_font_load_font(Display *display, const char *font) {
501   xitk_t *xitk = gXitk;
502   xitk_font_t           *xtfs;
503   xitk_font_list_item_t *list_item;
504 
505   ABORT_IF_NULL(display);
506   ABORT_IF_NULL(font);
507   pthread_mutex_lock(&xitk->font_cache->mutex);
508 
509   /* quick search in the cache of unloaded fonts */
510   if((xtfs = xitk_cache_take_item(display, font)) == NULL) {
511     /* search in the list of loaded fonts */
512     if((list_item = cache_get_from_list(display, font)) != NULL) {
513       list_item->number++;
514       pthread_mutex_unlock(&xitk->font_cache->mutex);
515       return list_item->font;
516     }
517   }
518 
519   if(!xtfs) {
520     /* font not found, load it */
521     xtfs = (xitk_font_t *) xitk_xmalloc(sizeof(xitk_font_t));
522 
523     if(!xitk_font_load_one(display, font, xtfs)) {
524       const char *fdname = xitk_get_default_font();
525 
526       if(!fdname || !xitk_font_load_one(display, fdname, xtfs)) {
527         const char *fsname = xitk_get_system_font();
528  	if(!xitk_font_load_one(display, fsname, xtfs)) {
529 	  XITK_WARNING("loading font \"%s\" failed, default and system fonts \"%s\" and \"%s\" failed too\n", font, fdname, fsname);
530 	  free(xtfs);
531 	  pthread_mutex_unlock(&xitk->font_cache->mutex);
532 
533 	  /* Maybe broken XMB support */
534 	  if(xitk_get_xmb_enability()) {
535 	    xitk_font_t *xtfs_fallback;
536 
537 	    xitk_set_xmb_enability(0);
538 	    if((xtfs_fallback = xitk_font_load_font(display, font)))
539 	      XITK_WARNING("XMB support seems broken on your system, add \"font.xmb = 0\" in your ~/.xitkrc\n");
540 
541 	    return xtfs_fallback;
542 	  }
543 	  else
544 	    return NULL;
545         }
546       }
547     }
548 
549     xtfs->name = strdup(font);
550     xtfs->display = display;
551 
552 #ifdef LOG
553     printf("xiTK: loaded new \"%s\", list: %zu, cache: %zu\n", xtfs->name, xitk->font_cache->nlist, xitk->font_cache->n);
554 #endif
555   }
556 
557   /* add the font into list */
558   list_item         = xitk_xmalloc(sizeof(xitk_font_list_item_t));
559   list_item->font   = xtfs;
560   list_item->number = 1;
561   xitk_dlist_add_tail (&xitk->font_cache->loaded, &list_item->node);
562   xitk->font_cache->nlist++;
563 
564   pthread_mutex_unlock(&xitk->font_cache->mutex);
565 
566   return xtfs;
567 }
568 
569 /*
570  *
571  */
xitk_font_unload_font(xitk_font_t * xtfs)572 void xitk_font_unload_font(xitk_font_t *xtfs) {
573   xitk_t *xitk = gXitk;
574   xitk_font_list_item_t *item;
575 
576   ABORT_IF_NULL(xtfs);
577   ABORT_IF_NULL(xtfs->display);
578 #ifndef WITH_XFT
579 #ifdef WITH_XMB
580   if(xitk_get_xmb_enability())
581     ABORT_IF_NULL(xtfs->fontset);
582   else
583 #endif
584 #endif
585     ABORT_IF_NULL(xtfs->font);
586 
587   pthread_mutex_lock(&xitk->font_cache->mutex);
588   /* search the font in the list */
589   item = cache_get_from_list(xtfs->display, xtfs->name);
590   /* it must be there */
591   ABORT_IF_NULL(item);
592 
593   if(--item->number == 0) {
594     xitk_dnode_remove (&item->node);
595     xitk->font_cache->nlist--;
596     xitk_cache_add_item(item->font);
597     free(item);
598   }
599 
600   pthread_mutex_unlock(&xitk->font_cache->mutex);
601 }
602 
603 /*
604  *
605  */
xitk_font_draw_string(xitk_font_t * xtfs,Pixmap pix,GC gc,int x,int y,const char * text,size_t nbytes)606 void xitk_font_draw_string(xitk_font_t *xtfs, Pixmap pix, GC gc,
607 			   int x, int y, const char *text,
608 			   size_t nbytes) {
609 
610   xitk_t *xitk = gXitk;
611 #ifdef DEBUG
612   if (nbytes > strlen(text) + 1) {
613     XITK_WARNING("draw: %zu > %zu\n", nbytes, strlen(text));
614   }
615 #endif
616 
617   XLOCK (xitk_x_lock_display, xtfs->display);
618 #ifndef WITH_XFT
619 # ifdef WITH_XMB
620   if(xitk_get_xmb_enability())
621     XmbDrawString(xtfs->display, pix, xtfs->fontset, gc, x, y, text, nbytes);
622   else
623 # endif
624     XDrawString(xtfs->display, pix, gc, x, y, text, nbytes);
625 #else
626   {
627     int           screen   = DefaultScreen( xtfs->display );
628     Visual       *visual   = DefaultVisual( xtfs->display, screen );
629     Colormap      colormap = DefaultColormap( xtfs->display, screen );
630     XGCValues     gc_color;
631     XColor        paint_color;
632     XRenderColor  xr_color;
633     XftColor      xcolor;
634     XftDraw      *xft_draw;
635     char          buf[2048];
636     xitk_recode_string_t rs;
637 
638     rs.src = text;
639     rs.ssize = strlen (text);
640     if ((size_t)nbytes < rs.ssize)
641       rs.ssize = nbytes;
642     rs.buf = buf;
643     rs.bsize = sizeof (buf);
644     xitk_recode2_do (xitk->font_cache->xr, &rs);
645 
646     XGetGCValues(xtfs->display, gc, GCForeground, &gc_color);
647     paint_color.pixel = gc_color.foreground;
648     XQueryColor(xtfs->display, colormap, &paint_color);
649     xr_color.red   = paint_color.red;
650     xr_color.green = paint_color.green;
651     xr_color.blue  = paint_color.blue;
652     xr_color.alpha = (short)-1;
653     xft_draw       = XftDrawCreate(xtfs->display, pix, visual, colormap);
654     XftColorAllocValue(xtfs->display, visual, colormap, &xr_color, &xcolor);
655     XftDrawStringUtf8(xft_draw, &xcolor, xtfs->font, x, y, (FcChar8 *)rs.res, rs.rsize);
656     XftColorFree(xtfs->display, visual, colormap, &xcolor);
657     XftDrawDestroy(xft_draw);
658 
659     xitk_recode2_done (xitk->font_cache->xr, &rs);
660   }
661 #endif
662   XUNLOCK (xitk_x_unlock_display, xtfs->display);
663 }
664 
665 #ifndef WITH_XFT
666 /*
667  *
668  */
xitk_font_get_font_id(xitk_font_t * xtfs)669 static Font xitk_font_get_font_id(xitk_font_t *xtfs) {
670 
671   ABORT_IF_NULL(xtfs);
672   ABORT_IF_NULL(xtfs->font);
673 
674   return xtfs->font->fid;
675 }
676 
677 /*
678  *
679  */
xitk_font_is_font_8(xitk_font_t * xtfs)680 static int xitk_font_is_font_8(xitk_font_t *xtfs) {
681 
682   ABORT_IF_NULL(xtfs);
683   ABORT_IF_NULL(xtfs->font);
684 
685   return ((xtfs->font->min_byte1 == 0) && (xtfs->font->max_byte1 == 0));
686 }
687 #endif
688 /*
689  *
690  */
691 #if 0
692 static int xitk_font_is_font_16(xitk_font_t *xtfs) {
693 
694   ABORT_IF_NULL(xtfs);
695   ABORT_IF_NULL(xtfs->font);
696 
697   return ((xtfs->font->min_byte1 != 0) || (xtfs->font->max_byte1 != 0));
698 }
699 #endif
700 
701 /*
702  *
703  */
xitk_font_get_text_width(xitk_font_t * xtfs,const char * c,int nbytes)704 int xitk_font_get_text_width(xitk_font_t *xtfs, const char *c, int nbytes) {
705   int width;
706 
707   ABORT_IF_NULL(xtfs);
708   ABORT_IF_NULL(xtfs->display);
709   ABORT_IF_NULL(c);
710 #ifdef WITH_XFT
711   ABORT_IF_NULL(xtfs->font);
712 
713   xitk_font_text_extent(xtfs, c, nbytes, NULL, NULL, &width, NULL, NULL);
714 # else
715 # ifdef WITH_XMB
716   if(xitk_get_xmb_enability()) {
717     ABORT_IF_NULL(xtfs->fontset);
718 
719     xitk_font_text_extent(xtfs, c, nbytes, NULL, NULL, &width, NULL, NULL);
720   }
721   else
722 # endif
723     {
724       ABORT_IF_NULL(xtfs->font);
725 
726       XLOCK (xitk_x_lock_display, xtfs->display);
727 
728       if(xitk_font_is_font_8(xtfs))
729 	width = XTextWidth (xtfs->font, c, nbytes);
730       else
731 	width = XTextWidth16 (xtfs->font, (XChar2b *)c, nbytes);
732 
733       XUNLOCK (xitk_x_unlock_display, xtfs->display);
734     }
735 #endif
736 
737   return width;
738 }
739 
740 /*
741  *
742  */
xitk_font_get_string_length(xitk_font_t * xtfs,const char * c)743 int xitk_font_get_string_length(xitk_font_t *xtfs, const char *c) {
744   return ((c && strlen(c)) ? (xitk_font_get_text_width(xtfs, c, strlen(c))) : 0);
745 }
746 
747 /*
748  *
749  */
xitk_font_get_char_width(xitk_font_t * xtfs,const char * c,int maxnbytes,int * nbytes)750 int xitk_font_get_char_width(xitk_font_t *xtfs, const char *c, int maxnbytes, int *nbytes) {
751 #ifndef WITH_XFT
752   unsigned int  ch = (*c & 0xff);
753   int           width;
754   XCharStruct  *chars;
755 #ifdef WITH_XMB
756   mbstate_t state;
757   size_t    n;
758 
759   if(xitk_get_xmb_enability()) {
760     ABORT_IF_NULL(xtfs);
761     ABORT_IF_NULL(xtfs->fontset);
762     ABORT_IF_NULL(c);
763 
764     if(maxnbytes <= 0)
765       return 0;
766 
767     memset(&state, '\0', sizeof(mbstate_t));
768     n = mbrtowc(NULL, c, maxnbytes, &state);
769 
770     if(n == (size_t)-1 || n == (size_t)-2)
771       return 0;
772 
773     if(nbytes)
774       *nbytes = n;
775 
776     return xitk_font_get_text_width(xtfs, c, n);
777   }
778   else
779 # endif
780     {
781       ABORT_IF_NULL(xtfs);
782       ABORT_IF_NULL(xtfs->font);
783       ABORT_IF_NULL(c);
784 
785       if(maxnbytes <= 0)
786 	return 0;
787 
788       if(xitk_font_is_font_8(xtfs) &&
789 	 ((ch >= xtfs->font->min_char_or_byte2) && (ch <= xtfs->font->max_char_or_byte2))) {
790 
791 	chars = xtfs->font->per_char;
792 
793 	if(chars)
794 	  width = chars[ch - xtfs->font->min_char_or_byte2].width;
795 	else
796 	  width = xtfs->font->min_bounds.width;
797 
798       }
799       else
800 	width = xitk_font_get_text_width(xtfs, c, 1);
801 
802       return width;
803     }
804 
805   return 0;
806 #endif
807   ABORT_IF_NULL(xtfs);
808   ABORT_IF_NULL(xtfs->font);
809   ABORT_IF_NULL(c);
810   return xitk_font_get_text_width(xtfs, c, 1);
811 }
812 
813 /*
814  *
815  */
xitk_font_get_text_height(xitk_font_t * xtfs,const char * c,int nbytes)816 int xitk_font_get_text_height(xitk_font_t *xtfs, const char *c, int nbytes) {
817   int ascent, descent;
818 
819   xitk_font_text_extent(xtfs, c, nbytes, NULL, NULL, NULL, &ascent, &descent);
820 
821   return (ascent + descent);
822 }
823 
824 /*
825  *
826  */
xitk_font_get_string_height(xitk_font_t * xtfs,const char * c)827 int xitk_font_get_string_height(xitk_font_t *xtfs, const char *c) {
828 
829   return (xitk_font_get_text_height(xtfs, c, strlen(c)));
830 }
831 
832 /*
833  *
834  */
xitk_font_get_char_height(xitk_font_t * xtfs,const char * c,int maxnbytes,int * nbytes)835 int xitk_font_get_char_height(xitk_font_t *xtfs, const char *c, int maxnbytes, int *nbytes) {
836 # ifdef WITH_XMB
837   mbstate_t state;
838   size_t    n;
839 
840   if(xitk_get_xmb_enability()) {
841     memset(&state, '\0', sizeof(mbstate_t));
842     n = mbrtowc(NULL, c, maxnbytes, &state);
843 
844     if(n == (size_t)-1 || n == (size_t)-2)
845       return 0;
846 
847     if(nbytes)
848       *nbytes = n;
849 
850     return xitk_font_get_text_height(xtfs, c, n);
851   }
852   else
853 #endif
854     return (xitk_font_get_text_height(xtfs, c, 1));
855 }
856 
857 /*
858  *
859  */
xitk_font_text_extent(xitk_font_t * xtfs,const char * c,int nbytes,int * lbearing,int * rbearing,int * width,int * ascent,int * descent)860 void xitk_font_text_extent(xitk_font_t *xtfs, const char *c, int nbytes,
861 			   int *lbearing, int *rbearing, int *width, int *ascent, int *descent) {
862   xitk_t *xitk = gXitk;
863 #ifndef WITH_XFT
864   XCharStruct ov;
865   int         dir;
866   int         fascent, fdescent;
867 #else
868   XGlyphInfo  xft_extents;
869 #endif
870 #if defined(WITH_XMB) && !defined(WITH_XFT)
871   XRectangle  ink, logic;
872 #endif
873 
874   if (nbytes <= 0) {
875 #ifdef DEBUG
876     if (nbytes < 0)
877       XITK_WARNING("nbytes < 0\n");
878 #endif
879     if (width) *width     = 0;
880     if (ascent) *ascent   = 0;
881     if (descent) *descent = 0;
882     if (lbearing) *lbearing = 0;
883     if (rbearing) *rbearing = 0;
884     return;
885   }
886 
887 #ifdef DEBUG
888   if ((size_t)nbytes > strlen(c) + 1) {
889     XITK_WARNING("extent: %d > %zu\n", nbytes, strlen(c));
890   }
891 #endif
892 
893 #ifdef WITH_XFT
894   ABORT_IF_NULL(xtfs);
895   ABORT_IF_NULL(xtfs->font);
896   ABORT_IF_NULL(c);
897 
898   /* recode right part of string */
899   {
900     char buf[2048];
901     xitk_recode_string_t rs;
902 
903     rs.src = c;
904     rs.ssize = strlen (c);
905     if ((size_t)nbytes < rs.ssize)
906       rs.ssize = nbytes;
907     rs.buf = buf;
908     rs.bsize = sizeof (buf);
909     xitk_recode2_do (xitk->font_cache->xr, &rs);
910 
911     /* FIXME: XftTextExtentsUtf8 () seems to be non reentrant - at least when called
912      * with same font. We probably need to mutex it when XLockDisplay () is turned off. */
913     XLOCK (xitk_x_lock_display, xtfs->display);
914     XftTextExtentsUtf8 (xtfs->display, xtfs->font, (FcChar8 *)rs.res, rs.rsize, &xft_extents);
915     XUNLOCK (xitk_x_unlock_display, xtfs->display);
916 
917     xitk_recode2_done (xitk->font_cache->xr, &rs);
918   }
919 
920   if (width) *width       = xft_extents.xOff;
921   /* Since Xft fonts have more top and bottom space rows than Xmb/Xcore fonts, */
922   /* we tweak ascent and descent to appear the same like Xmb/Xcore, so we get  */
923   /* unified vertical text positions. We must however be aware to eventually   */
924   /* reserve space for these rows when drawing the text.                       */
925   /* Following trick works for our font sizes 10...14.                         */
926   if (ascent) *ascent     = xtfs->font->ascent - (xtfs->font->ascent - 9) / 2;
927   if (descent) *descent   = xtfs->font->descent - (xtfs->font->descent - 0) / 2;
928   if (lbearing) *lbearing = -xft_extents.x;
929   if (rbearing) *rbearing = -xft_extents.x + xft_extents.width;
930 #else
931 #ifdef WITH_XMB
932   if(xitk_get_xmb_enability()) {
933     ABORT_IF_NULL(xtfs);
934     ABORT_IF_NULL(xtfs->fontset);
935     ABORT_IF_NULL(c);
936 
937     XLOCK (xitk_x_lock_display, xtfs->display);
938     XmbTextExtents(xtfs->fontset, c, nbytes, &ink, &logic);
939     XUNLOCK (xitk_x_unlock_display, xtfs->display);
940     if (!logic.width || !logic.height) {
941       /* XmbTextExtents() has problems, try char-per-char counting */
942       mbstate_t state;
943       size_t    i = 0, n;
944       int       height = 0, width = 0, y = 0, lb = 0, rb;
945 
946       memset(&state, '\0', sizeof(mbstate_t));
947 
948       XLOCK (xitk_x_lock_display, xtfs->display);
949       while (i < nbytes) {
950 	n = mbrtowc(NULL, c + i, nbytes - i, &state);
951 	if (n == (size_t)-1 || n == (size_t)-2 || i + n > nbytes) n = 1;
952 	XmbTextExtents(xtfs->fontset, c + i, n, &ink, &logic);
953 	if (logic.height > height) height = logic.height;
954 	if (logic.y < y) y = logic.y; /* y is negative! */
955 	if (i == 0) lb = ink.x; /* left bearing of string */
956 	width += logic.width;
957 	i += n;
958       }
959       rb = width - logic.width + ink.x + ink.width; /* right bearing of string */
960       XUNLOCK (xitk_x_unlock_display, xtfs->display);
961 
962       if (!height || !width) {
963 	/* char-per-char counting fails too */
964 	XITK_WARNING("extents of the font \"%s\" are %dx%d!\n", xtfs->name, width, height);
965 	if (!height) height = 12, y = -10;
966 	if (!width) lb = rb = 0;
967       }
968 
969       logic.height = height;
970       logic.width = width;
971       logic.y = y;
972       ink.x = lb;
973       ink.width = rb - lb;
974     }
975 
976     if (width) *width     = logic.width;
977     if (ascent) *ascent   = -logic.y;
978     if (descent) *descent = logic.height + logic.y;
979     if (lbearing) *lbearing = ink.x;
980     if (rbearing) *rbearing = ink.x + ink.width;
981   }
982   else
983 #endif
984     {
985       ABORT_IF_NULL(xtfs);
986       ABORT_IF_NULL(xtfs->font);
987       ABORT_IF_NULL(xtfs->display);
988       ABORT_IF_NULL(c);
989 
990       XLOCK (xitk_x_lock_display, xtfs->display);
991       if(xitk_font_is_font_8(xtfs))
992 	XTextExtents(xtfs->font, c, nbytes, &dir, &fascent, &fdescent, &ov);
993       else
994 	XTextExtents16(xtfs->font, (XChar2b *)c, (nbytes / 2), &dir, &fascent, &fdescent, &ov);
995       XUNLOCK (xitk_x_unlock_display, xtfs->display);
996 
997       if(lbearing)
998 	*lbearing = ov.lbearing;
999       if(rbearing)
1000 	*rbearing = ov.rbearing;
1001       if(width)
1002 	*width    = ov.width;
1003       if(ascent)
1004 	*ascent  = xtfs->font->ascent;
1005       if(descent)
1006 	*descent = xtfs->font->descent;
1007 #if 0
1008       if(ascent)
1009 	*ascent   = ov.ascent;
1010       if(descent)
1011 	*descent  = ov.descent;
1012 #endif
1013     }
1014 #endif
1015 }
1016 
1017 /*
1018  *
1019  */
xitk_font_string_extent(xitk_font_t * xtfs,const char * c,int * lbearing,int * rbearing,int * width,int * ascent,int * descent)1020 void xitk_font_string_extent(xitk_font_t *xtfs, const char *c,
1021 			     int *lbearing, int *rbearing, int *width, int *ascent, int *descent) {
1022 
1023   xitk_font_text_extent(xtfs, c, strlen(c), lbearing, rbearing, width, ascent, descent);
1024 }
1025 
1026 /*
1027  *
1028  */
xitk_font_get_ascent(xitk_font_t * xtfs,const char * c)1029 int xitk_font_get_ascent(xitk_font_t *xtfs, const char *c) {
1030   int ascent;
1031 
1032   ABORT_IF_NULL(xtfs);
1033   ABORT_IF_NULL(xtfs->display);
1034   ABORT_IF_NULL(c);
1035 #ifndef WITH_XFT
1036 #ifdef WITH_XMB
1037   if(xitk_get_xmb_enability())
1038     ABORT_IF_NULL(xtfs->fontset);
1039   else
1040 #endif
1041 #endif
1042     ABORT_IF_NULL(xtfs->font);
1043 
1044   xitk_font_text_extent(xtfs, c, strlen(c), NULL, NULL, NULL, &ascent, NULL);
1045 
1046   return ascent;
1047 }
1048 
1049 /*
1050  *
1051  */
xitk_font_get_descent(xitk_font_t * xtfs,const char * c)1052 int xitk_font_get_descent(xitk_font_t *xtfs, const char *c) {
1053   int descent;
1054 
1055   ABORT_IF_NULL(xtfs);
1056   ABORT_IF_NULL(xtfs->display);
1057   ABORT_IF_NULL(c);
1058 #ifndef WITH_XFT
1059 #ifdef WITH_XMB
1060   if(xitk_get_xmb_enability())
1061     ABORT_IF_NULL(xtfs->fontset);
1062   else
1063 #endif
1064 #endif
1065     ABORT_IF_NULL(xtfs->font);
1066 
1067   xitk_font_text_extent(xtfs, c, strlen(c), NULL, NULL, NULL, NULL, &descent);
1068 
1069   return descent;
1070 }
1071 
1072 /*
1073  *
1074  */
xitk_font_set_font(xitk_font_t * xtfs,GC gc)1075 void xitk_font_set_font(xitk_font_t *xtfs, GC gc) {
1076 
1077   ABORT_IF_NULL(xtfs);
1078   ABORT_IF_NULL(xtfs->display);
1079 #ifndef WITH_XFT
1080 # ifdef WITH_XMB
1081   if(xitk_get_xmb_enability())
1082     ;
1083   else
1084 # endif
1085     {
1086       XLOCK (xitk_x_lock_display, xtfs->display);
1087       XSetFont(xtfs->display, gc, xitk_font_get_font_id(xtfs));
1088       XUNLOCK (xitk_x_unlock_display, xtfs->display);
1089     }
1090 #else
1091   (void)gc;
1092 #endif
1093 }
1094