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