1 /*
2  * Copyright © 2000 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Keith Packard not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Keith Packard makes no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #include "xftint.h"
24 
25 _X_HIDDEN FT_Library  _XftFTlibrary;
26 
27 #define FT_Matrix_Equal(a,b)	((a)->xx == (b)->xx && \
28 				 (a)->yy == (b)->yy && \
29 				 (a)->xy == (b)->xy && \
30 				 (a)->yx == (b)->yx)
31 /*
32  * List of all open files (each face in a file is managed separately)
33  */
34 
35 static XftFtFile *_XftFtFiles;
36 static int XftMaxFreeTypeFiles = 5;
37 
38 static XftFtFile *
_XftGetFile(const FcChar8 * file,int id)39 _XftGetFile (const FcChar8 *file, int id)
40 {
41     XftFtFile	*f;
42 
43     if (!XftInitFtLibrary ())
44 	return NULL;
45 
46     for (f = _XftFtFiles; f; f = f->next)
47     {
48 	if (!strcmp (f->file, (char *) file) && f->id == id)
49 	{
50 	    ++f->ref;
51 	    if (XftDebug () & XFT_DBG_REF)
52 		printf ("FontFile %s/%d matches existing (%d)\n",
53 			file, id, f->ref);
54 	    return f;
55 	}
56     }
57     f = malloc (sizeof (XftFtFile) + strlen ((char *) file) + 1);
58     if (!f)
59 	return NULL;
60 
61     XftMemAlloc (XFT_MEM_FILE, (int)(sizeof (XftFtFile) + strlen ((char *) file) + 1));
62     if (XftDebug () & XFT_DBG_REF)
63     	printf ("FontFile %s/%d matches new\n",
64 		file, id);
65     f->next = _XftFtFiles;
66     _XftFtFiles = f;
67 
68     f->ref = 1;
69 
70     f->file = (char *) (f+1);
71     strcpy (f->file, (char *) file);
72     f->id = id;
73 
74     f->lock = 0;
75     f->face = NULL;
76     f->xsize = 0;
77     f->ysize = 0;
78     f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0;
79     return f;
80 }
81 
82 static XftFtFile *
_XftGetFaceFile(FT_Face face)83 _XftGetFaceFile (FT_Face face)
84 {
85     XftFtFile	*f;
86 
87     f = malloc (sizeof (XftFtFile));
88     if (!f)
89 	return NULL;
90     XftMemAlloc (XFT_MEM_FILE, sizeof(XftFtFile));
91     f->next = NULL;
92 
93     f->ref = 1;
94 
95     f->file = NULL;
96     f->id = 0;
97     f->lock = 0;
98     f->face = face;
99     f->xsize = 0;
100     f->ysize = 0;
101     f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0;
102     return f;
103 }
104 
105 static int
_XftNumFiles(void)106 _XftNumFiles (void)
107 {
108     XftFtFile	*f;
109     int		count = 0;
110     for (f = _XftFtFiles; f; f = f->next)
111 	if (f->face && !f->lock)
112 	    ++count;
113     return count;
114 }
115 
116 static XftFtFile *
_XftNthFile(int n)117 _XftNthFile (int n)
118 {
119     XftFtFile	*f;
120     int		count = 0;
121     for (f = _XftFtFiles; f; f = f->next)
122 	if (f->face && !f->lock)
123 	    if (count++ == n)
124 		break;
125     return f;
126 }
127 
128 static void
_XftUncacheFiles(void)129 _XftUncacheFiles (void)
130 {
131     int		n;
132     XftFtFile	*f;
133     while ((n = _XftNumFiles ()) > XftMaxFreeTypeFiles)
134     {
135 	f = _XftNthFile (rand () % n);
136 	if (f)
137 	{
138 	    if (XftDebug() & XFT_DBG_REF)
139 		printf ("Discard file %s/%d from cache\n",
140 			f->file, f->id);
141 	    FT_Done_Face (f->face);
142 	    f->face = NULL;
143 	}
144     }
145 }
146 
147 static FT_Face
_XftLockFile(XftFtFile * f)148 _XftLockFile (XftFtFile *f)
149 {
150     ++f->lock;
151     if (!f->face)
152     {
153 	if (XftDebug() & XFT_DBG_REF)
154 	    printf ("Loading file %s/%d\n", f->file, f->id);
155 	if (FT_New_Face (_XftFTlibrary, f->file, f->id, &f->face))
156 	    --f->lock;
157 
158 	f->xsize = 0;
159 	f->ysize = 0;
160 	f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0;
161 	_XftUncacheFiles ();
162     }
163     return f->face;
164 }
165 
166 static void
_XftLockError(const char * reason)167 _XftLockError (const char *reason)
168 {
169     fprintf (stderr, "Xft: locking error %s\n", reason);
170 }
171 
172 static void
_XftUnlockFile(XftFtFile * f)173 _XftUnlockFile (XftFtFile *f)
174 {
175     if (--f->lock < 0)
176 	_XftLockError ("too many file unlocks");
177 }
178 
179 #define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem)
180 #define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem)
181 
182 _X_HIDDEN FcBool
_XftSetFace(XftFtFile * f,FT_F26Dot6 xsize,FT_F26Dot6 ysize,FT_Matrix * matrix)183 _XftSetFace (XftFtFile *f, FT_F26Dot6 xsize, FT_F26Dot6 ysize, FT_Matrix *matrix)
184 {
185     FT_Face face = f->face;
186 
187     if (f->xsize != xsize || f->ysize != ysize)
188     {
189 	if (XftDebug() & XFT_DBG_GLYPH)
190 	    printf ("Set face size to %dx%d (%dx%d)\n",
191 		    (int) (xsize >> 6), (int) (ysize >> 6), (int) xsize, (int) ysize);
192 	/*
193 	 * Bitmap only faces must match exactly, so find the closest
194 	 * one (height dominant search)
195 	 */
196 	if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
197 	{
198 	    int		i, best = 0;
199 
200 #define xft_abs(a)	((a) < 0 ? -(a) : (a))
201 #define dist(a,b)	(xft_abs((a)-(b)))
202 
203 	    for (i = 1; i < face->num_fixed_sizes; i++)
204 	    {
205 		if (dist (ysize, Y_SIZE(face,i)) <
206 		    dist (ysize, Y_SIZE(face, best)) ||
207 		    (dist (ysize, Y_SIZE(face, i)) ==
208 		     dist (ysize, Y_SIZE(face, best)) &&
209 		     dist (xsize, X_SIZE(face, i)) <
210 		     dist (xsize, X_SIZE(face, best))))
211 		{
212 		    best = i;
213 		}
214 	    }
215 	    /*
216 	     * Freetype 2.1.7 and earlier used width/height
217 	     * for matching sizes in the BDF and PCF loaders.
218 	     * This has been fixed for 2.1.8.  Because BDF and PCF
219 	     * files have but a single strike per file, we can
220 	     * simply try both sizes.
221 	     */
222 	    if (FT_Set_Char_Size (face, face->available_sizes[best].x_ppem,
223 				  face->available_sizes[best].y_ppem, 0, 0) != 0
224 		&&
225 		FT_Set_Char_Size (face, face->available_sizes[best].width << 6,
226 				  face->available_sizes[best].height << 6,
227 				  0, 0) != 0)
228 	    {
229 		return False;
230 	    }
231 	}
232 	else
233     	{
234 	    if (FT_Set_Char_Size (face, xsize, ysize, 0, 0))
235 	    {
236 		return False;
237 	    }
238 	}
239 	f->xsize = xsize;
240 	f->ysize = ysize;
241     }
242     if (!FT_Matrix_Equal (&f->matrix, matrix))
243     {
244 	if (XftDebug() & XFT_DBG_GLYPH)
245 	    printf ("Set face matrix to (%g,%g,%g,%g)\n",
246 		    (double) matrix->xx / 0x10000,
247 		    (double) matrix->xy / 0x10000,
248 		    (double) matrix->yx / 0x10000,
249 		    (double) matrix->yy / 0x10000);
250 	FT_Set_Transform (face, matrix, NULL);
251 	f->matrix = *matrix;
252     }
253     return True;
254 }
255 
256 static void
_XftReleaseFile(XftFtFile * f)257 _XftReleaseFile (XftFtFile *f)
258 {
259     XftFtFile	**prev;
260 
261     if (--f->ref != 0)
262         return;
263     if (f->lock)
264 	_XftLockError ("Attempt to close locked file");
265     if (f->file)
266     {
267 	for (prev = &_XftFtFiles; *prev; prev = &(*prev)->next)
268 	{
269 	    if (*prev == f)
270 	    {
271 		*prev = f->next;
272 		break;
273 	    }
274 	}
275 	if (f->face)
276 	    FT_Done_Face (f->face);
277     }
278     XftMemFree (XFT_MEM_FILE,
279 		(sizeof (XftFtFile) + (f->file ? strlen (f->file) + 1 : 0)));
280     free (f);
281 }
282 
283 /*
284  * Find a prime larger than the minimum reasonable hash size
285  */
286 
287 static FcChar32
_XftSqrt(FcChar32 a)288 _XftSqrt (FcChar32 a)
289 {
290     FcChar32	    l, h, m;
291 
292     l = 2;
293     h = a/2;
294     while ((h-l) > 1)
295     {
296 	m = (h+l) >> 1;
297 	if (m * m < a)
298 	    l = m;
299 	else
300 	    h = m;
301     }
302     return h;
303 }
304 
305 static FcBool
_XftIsPrime(FcChar32 i)306 _XftIsPrime (FcChar32 i)
307 {
308     FcChar32	l, t;
309 
310     if (i < 2)
311 	return FcFalse;
312     if ((i & 1) == 0)
313     {
314 	if (i == 2)
315 	    return FcTrue;
316 	return FcFalse;
317     }
318     l = _XftSqrt (i) + 1;
319     for (t = 3; t <= l; t += 2)
320 	if (i % t == 0)
321 	    return FcFalse;
322     return FcTrue;
323 }
324 
325 static FcChar32
_XftHashSize(FcChar32 num_unicode)326 _XftHashSize (FcChar32 num_unicode)
327 {
328     /* at least 31.25 % extra space */
329     FcChar32	hash = num_unicode + (num_unicode >> 2) + (num_unicode >> 4);
330 
331     if ((hash & 1) == 0)
332 	hash++;
333     while (!_XftIsPrime (hash))
334 	hash += 2;
335     return hash;
336 }
337 
338 _X_EXPORT FT_Face
XftLockFace(XftFont * public)339 XftLockFace (XftFont *public)
340 {
341     XftFontInt	*font = (XftFontInt *) public;
342     XftFontInfo	*fi = &font->info;
343     FT_Face	face;
344 
345     face = _XftLockFile (fi->file);
346     /*
347      * Make sure the face is usable at the requested size
348      */
349     if (face && !_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix))
350     {
351 	_XftUnlockFile (fi->file);
352 	face = NULL;
353     }
354     return face;
355 }
356 
357 _X_EXPORT void
XftUnlockFace(XftFont * public)358 XftUnlockFace (XftFont *public)
359 {
360     XftFontInt	*font = (XftFontInt *) public;
361     _XftUnlockFile (font->info.file);
362 }
363 
364 static FcBool
XftFontInfoFill(Display * dpy,_Xconst FcPattern * pattern,XftFontInfo * fi)365 XftFontInfoFill (Display *dpy, _Xconst FcPattern *pattern, XftFontInfo *fi)
366 {
367     XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, True);
368     FcChar8	    *filename;
369     int		    id;
370     double	    dsize;
371     double	    aspect;
372     FcMatrix	    *font_matrix;
373     FcBool	    hinting, vertical_layout, autohint, global_advance;
374     int             hint_style;
375     FcChar32	    hash, *hashp;
376     FT_Face	    face;
377     int		    nhash;
378     FcBool	    bitmap;
379 
380     if (!info)
381 	return FcFalse;
382 
383     /*
384      * Initialize the whole XftFontInfo so that padding doesn't interfere with
385      * hash or XftFontInfoEqual().
386      */
387 
388     memset (fi, '\0', sizeof(*fi));
389 
390     /*
391      * Find the associated file
392      */
393     switch (FcPatternGetString (pattern, FC_FILE, 0, &filename)) {
394     case FcResultNoMatch:
395 	filename = NULL;
396 	break;
397     case FcResultMatch:
398 	break;
399     default:
400 	goto bail0;
401     }
402 
403     switch (FcPatternGetInteger (pattern, FC_INDEX, 0, &id)) {
404     case FcResultNoMatch:
405 	id = 0;
406 	break;
407     case FcResultMatch:
408 	break;
409     default:
410 	goto bail0;
411     }
412 
413     if (filename)
414 	fi->file = _XftGetFile (filename, id);
415     else if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &face) == FcResultMatch
416 	     && face)
417 	fi->file = _XftGetFaceFile (face);
418     if (!fi->file)
419         goto bail0;
420 
421     /*
422      * Compute pixel size
423      */
424     if (FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
425 	goto bail1;
426 
427     if (FcPatternGetDouble (pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
428 	aspect = 1.0;
429 
430     fi->ysize = (FT_F26Dot6) (dsize * 64.0);
431     fi->xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
432 
433     if (XftDebug() & XFT_DBG_OPEN)
434 	printf ("XftFontInfoFill: %s: %d (%g pixels)\n",
435 		(filename ? filename : (FcChar8 *) "<none>"), id, dsize);
436     /*
437      * Get antialias value
438      */
439     switch (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &fi->antialias)) {
440     case FcResultNoMatch:
441 	fi->antialias = True;
442 	break;
443     case FcResultMatch:
444 	break;
445     default:
446 	goto bail1;
447     }
448 
449     /*
450      * Get rgba value
451      */
452     switch (FcPatternGetInteger (pattern, FC_RGBA, 0, &fi->rgba)) {
453     case FcResultNoMatch:
454 	fi->rgba = FC_RGBA_UNKNOWN;
455 	break;
456     case FcResultMatch:
457 	break;
458     default:
459 	goto bail1;
460     }
461 
462     /*
463      * Get lcd_filter value
464      */
465     switch (FcPatternGetInteger (pattern, FC_LCD_FILTER, 0, &fi->lcd_filter)) {
466     case FcResultNoMatch:
467 	fi->lcd_filter = FC_LCD_DEFAULT;
468 	break;
469     case FcResultMatch:
470 	break;
471     default:
472 	goto bail1;
473     }
474 
475     /*
476      * Get matrix and transform values
477      */
478     switch (FcPatternGetMatrix (pattern, FC_MATRIX, 0, &font_matrix)) {
479     case FcResultNoMatch:
480 	fi->matrix.xx = fi->matrix.yy = 0x10000;
481 	fi->matrix.xy = fi->matrix.yx = 0;
482 	break;
483     case FcResultMatch:
484 	fi->matrix.xx = (FT_Fixed)(0x10000L * font_matrix->xx);
485 	fi->matrix.yy = (FT_Fixed)(0x10000L * font_matrix->yy);
486 	fi->matrix.xy = (FT_Fixed)(0x10000L * font_matrix->xy);
487 	fi->matrix.yx = (FT_Fixed)(0x10000L * font_matrix->yx);
488 	break;
489     default:
490 	goto bail1;
491     }
492 
493     fi->transform = (fi->matrix.xx != 0x10000 || fi->matrix.xy != 0 ||
494 		     fi->matrix.yx != 0 || fi->matrix.yy != 0x10000);
495 
496     /*
497      * Get render value, set to false if no Render extension present
498      */
499     if (info->hasRender)
500     {
501 	switch (FcPatternGetBool (pattern, XFT_RENDER, 0, &fi->render)) {
502 	case FcResultTypeMismatch:
503 	    /*
504 	     * Fontconfig no longer supports xft's custom values in
505 	     * text patterns, so any name specifying render:true or
506 	     * render:false will have an invalid type in the resulting
507 	     * pattern. Just ignore that case so that the app doesn't
508 	     * just fail
509 	     */
510 	    /* fall through ... */
511 	case FcResultNoMatch:
512 	    fi->render = info->hasRender;
513 	    break;
514 	case FcResultMatch:
515 	    break;
516 	default:
517 	    goto bail1;
518 	}
519     }
520     else
521 	fi->render = FcFalse;
522 
523     /*
524      * Compute glyph load flags
525      */
526     fi->load_flags = FT_LOAD_DEFAULT;
527 
528 #ifndef XFT_EMBEDDED_BITMAP
529 #define XFT_EMBEDDED_BITMAP "embeddedbitmap"
530 #endif
531 
532     switch (FcPatternGetBool (pattern, XFT_EMBEDDED_BITMAP, 0, &bitmap)) {
533     case FcResultNoMatch:
534 	bitmap = FcFalse;
535 	break;
536     case FcResultMatch:
537 	break;
538     default:
539 	goto bail1;
540     }
541 
542     /* disable bitmaps when anti-aliasing or transforming glyphs */
543     if ((!bitmap && fi->antialias) || fi->transform)
544 	fi->load_flags |= FT_LOAD_NO_BITMAP;
545 
546     /* disable hinting if requested */
547     switch (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting)) {
548     case FcResultNoMatch:
549 	hinting = FcTrue;
550 	break;
551     case FcResultMatch:
552 	break;
553     default:
554 	goto bail1;
555     }
556 
557     switch (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &fi->embolden)) {
558     case FcResultNoMatch:
559 	fi->embolden = FcFalse;
560 	break;
561     case FcResultMatch:
562 	break;
563     default:
564 	goto bail1;
565     }
566 
567     switch (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style)) {
568     case FcResultNoMatch:
569 	hint_style = FC_HINT_FULL;
570 	break;
571     case FcResultMatch:
572 	break;
573     default:
574 	goto bail1;
575     }
576 
577     if (!hinting
578 	|| hint_style == FC_HINT_NONE
579 	)
580     {
581 	fi->load_flags |= FT_LOAD_NO_HINTING;
582     }
583 
584     /* Figure out the load target, which modifies the hinting
585      * behavior of FreeType based on the intended use of the glyphs.
586      */
587     if (fi->antialias)
588     {
589 	if (FC_HINT_NONE < hint_style && hint_style < FC_HINT_FULL)
590 	{
591 	    fi->load_flags |= FT_LOAD_TARGET_LIGHT;
592 	}
593 	else
594 	{
595 	    /* autohinter will snap stems to integer widths, when
596 	     * the LCD targets are used.
597 	     */
598 	    switch (fi->rgba) {
599 	    case FC_RGBA_RGB:
600 	    case FC_RGBA_BGR:
601 		fi->load_flags |= FT_LOAD_TARGET_LCD;
602 		break;
603 	    case FC_RGBA_VRGB:
604 	    case FC_RGBA_VBGR:
605 		fi->load_flags |= FT_LOAD_TARGET_LCD_V;
606 		break;
607 	    }
608 	}
609     }
610     else
611 	fi->load_flags |= FT_LOAD_TARGET_MONO;
612 
613     /* set vertical layout if requested */
614     switch (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout)) {
615     case FcResultNoMatch:
616 	vertical_layout = FcFalse;
617 	break;
618     case FcResultMatch:
619 	break;
620     default:
621 	goto bail1;
622     }
623 
624     if (vertical_layout)
625 	fi->load_flags |= FT_LOAD_VERTICAL_LAYOUT;
626 
627     /* force autohinting if requested */
628     switch (FcPatternGetBool (pattern, FC_AUTOHINT, 0, &autohint)) {
629     case FcResultNoMatch:
630 	autohint = FcFalse;
631 	break;
632     case FcResultMatch:
633 	break;
634     default:
635 	goto bail1;
636     }
637 
638     if (autohint)
639 	fi->load_flags |= FT_LOAD_FORCE_AUTOHINT;
640 
641     /* disable global advance width (for broken DynaLab TT CJK fonts) */
642     switch (FcPatternGetBool (pattern, FC_GLOBAL_ADVANCE, 0, &global_advance)) {
643     case FcResultNoMatch:
644 	global_advance = FcTrue;
645 	break;
646     case FcResultMatch:
647 	break;
648     default:
649 	goto bail1;
650     }
651 
652     if (!global_advance)
653 	fi->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
654 
655     /*
656      * Get requested spacing value
657      */
658     switch (FcPatternGetInteger (pattern, FC_SPACING, 0, &fi->spacing)) {
659     case FcResultNoMatch:
660 	fi->spacing = FC_PROPORTIONAL;
661 	break;
662     case FcResultMatch:
663 	break;
664     default:
665 	goto bail1;
666     }
667 
668     /*
669      * Check for minspace
670      */
671 
672     switch (FcPatternGetBool (pattern, FC_MINSPACE, 0, &fi->minspace)) {
673     case FcResultNoMatch:
674 	fi->minspace = FcFalse;
675 	break;
676     case FcResultMatch:
677 	break;
678     default:
679 	goto bail1;
680     }
681     /*
682      * Check for fixed pixel spacing
683      */
684     switch (FcPatternGetInteger (pattern, FC_CHAR_WIDTH, 0, &fi->char_width)) {
685     case FcResultNoMatch:
686 	fi->char_width = 0;
687 	break;
688     case FcResultMatch:
689 	if (fi->char_width)
690 	    fi->spacing = FC_MONO;
691 	break;
692     default:
693 	goto bail1;
694     }
695 
696     /*
697      * Step over hash value in the structure
698      */
699     hash = 0;
700     hashp = (FcChar32 *) fi + 1;
701     nhash = (sizeof (XftFontInfo) / sizeof (FcChar32)) - 1;
702 
703     while (nhash--)
704 	hash += *hashp++;
705     fi->hash = hash;
706 
707     /*
708      * All done
709      */
710     return FcTrue;
711 
712 bail1:
713     _XftReleaseFile (fi->file);
714     fi->file = NULL;
715 bail0:
716     return FcFalse;
717 }
718 
719 static void
XftFontInfoEmpty(Display * dpy,XftFontInfo * fi)720 XftFontInfoEmpty (Display *dpy, XftFontInfo *fi)
721 {
722     if (fi->file)
723 	_XftReleaseFile (fi->file);
724 }
725 
726 XftFontInfo *
XftFontInfoCreate(Display * dpy,_Xconst FcPattern * pattern)727 XftFontInfoCreate (Display *dpy, _Xconst FcPattern *pattern)
728 {
729     XftFontInfo	*fi = malloc (sizeof (XftFontInfo));
730 
731     if (!fi)
732 	return NULL;
733 
734     if (!XftFontInfoFill (dpy, pattern, fi))
735     {
736 	free (fi);
737 	fi = NULL;
738     }
739     XftMemAlloc (XFT_MEM_FONT, sizeof (XftFontInfo));
740     return fi;
741 }
742 
743 _X_EXPORT void
XftFontInfoDestroy(Display * dpy,XftFontInfo * fi)744 XftFontInfoDestroy (Display *dpy, XftFontInfo *fi)
745 {
746     XftFontInfoEmpty (dpy, fi);
747     XftMemFree (XFT_MEM_FONT, sizeof (XftFontInfo));
748     free (fi);
749 }
750 
751 _X_EXPORT FcChar32
XftFontInfoHash(_Xconst XftFontInfo * fi)752 XftFontInfoHash (_Xconst XftFontInfo *fi)
753 {
754     return fi->hash;
755 }
756 
757 _X_EXPORT FcBool
XftFontInfoEqual(_Xconst XftFontInfo * a,_Xconst XftFontInfo * b)758 XftFontInfoEqual (_Xconst XftFontInfo *a, _Xconst XftFontInfo *b)
759 {
760     return memcmp ((void *) a, (void *) b, sizeof (XftFontInfo)) == 0;
761 }
762 
763 _X_EXPORT XftFont *
XftFontOpenInfo(Display * dpy,FcPattern * pattern,XftFontInfo * fi)764 XftFontOpenInfo (Display	*dpy,
765 		 FcPattern	*pattern,
766 		 XftFontInfo	*fi)
767 {
768     XftDisplayInfo	*info = _XftDisplayInfoGet (dpy, True);
769     FT_Face		face;
770     XftFont		**bucket;
771     XftFontInt		*font;
772     XRenderPictFormat	*format;
773     FcCharSet		*charset;
774     FcChar32		num_unicode;
775     FcChar32		hash_value;
776     FcChar32		rehash_value;
777     FcBool		antialias;
778     int			max_glyph_memory;
779     int			alloc_size;
780     int			ascent, descent, height;
781     int			i;
782     int			num_glyphs;
783 
784     if (!info)
785 	return NULL;
786     /*
787      * Find a matching previously opened font
788      */
789     bucket = &info->fontHash[fi->hash % XFT_NUM_FONT_HASH];
790     for (font = (XftFontInt *) *bucket; font; font = (XftFontInt *) font->hash_next)
791 	if (XftFontInfoEqual (&font->info, fi))
792 	{
793 	    if (!font->ref++)
794 		--info->num_unref_fonts;
795 	    FcPatternDestroy (pattern);
796 	    return &font->public;
797 	}
798 
799     /*
800      * No existing font, create another.
801      */
802 
803     if (XftDebug () & XFT_DBG_CACHE)
804 	printf ("New font %s/%d size %dx%d\n",
805 		fi->file->file, fi->file->id,
806 		(int) fi->xsize >> 6, (int) fi->ysize >> 6);
807 
808     if (FcPatternGetInteger (pattern, XFT_MAX_GLYPH_MEMORY, 0,
809 			     &max_glyph_memory) != FcResultMatch)
810 	max_glyph_memory = XFT_FONT_MAX_GLYPH_MEMORY;
811 
812     face = _XftLockFile (fi->file);
813     if (!face)
814 	goto bail0;
815 
816     if (!_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix))
817 	goto bail1;
818 
819     /*
820      * Get the set of Unicode codepoints covered by the font.
821      * If the incoming pattern doesn't provide this data, go
822      * off and compute it.  Yes, this is expensive, but it's
823      * required to map Unicode to glyph indices.
824      */
825     if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) == FcResultMatch)
826 	charset = FcCharSetCopy (charset);
827     else
828 	charset = FcFreeTypeCharSet (face, FcConfigGetBlanks (NULL));
829 
830     antialias = fi->antialias;
831     if (!(face->face_flags & FT_FACE_FLAG_SCALABLE))
832 	antialias = FcFalse;
833 
834     /*
835      * Find the appropriate picture format
836      */
837     if (fi->render)
838     {
839 	if (antialias)
840 	{
841 	    switch (fi->rgba) {
842 	    case FC_RGBA_RGB:
843 	    case FC_RGBA_BGR:
844 	    case FC_RGBA_VRGB:
845 	    case FC_RGBA_VBGR:
846 		format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
847 		break;
848 	    default:
849 		format = XRenderFindStandardFormat (dpy, PictStandardA8);
850 		break;
851 	    }
852 	}
853 	else
854 	{
855 	    format = XRenderFindStandardFormat (dpy, PictStandardA1);
856 	}
857 
858 	if (!format)
859 	    goto bail2;
860     }
861     else
862 	format = NULL;
863 
864     if (charset)
865     {
866 	num_unicode = FcCharSetCount (charset);
867 	hash_value = _XftHashSize (num_unicode);
868 	rehash_value = hash_value - 2;
869     }
870     else
871     {
872 	num_unicode = 0;
873 	hash_value = 0;
874 	rehash_value = 0;
875     }
876 
877     /*
878      * Sometimes the glyphs are numbered 1..n, other times 0..n-1,
879      * accept either numbering scheme by making room in the table
880      */
881     num_glyphs = (int)face->num_glyphs + 1;
882     alloc_size = (sizeof (XftFontInt) +
883 		  (size_t)num_glyphs * sizeof (XftGlyph *) +
884 		  hash_value * sizeof (XftUcsHash));
885     font = malloc ((size_t)alloc_size);
886 
887     if (!font)
888 	goto bail2;
889 
890     XftMemAlloc (XFT_MEM_FONT, alloc_size);
891 
892     /*
893      * Public fields
894      */
895     if (fi->transform)
896     {
897 	FT_Vector	vector;
898 
899 	vector.x = 0;
900 	vector.y = face->size->metrics.descender;
901 	FT_Vector_Transform (&vector, &fi->matrix);
902 	descent = (int)(-(vector.y >> 6));
903 
904 	vector.x = 0;
905 	vector.y = face->size->metrics.ascender;
906 	FT_Vector_Transform (&vector, &fi->matrix);
907 	ascent = (int)(vector.y >> 6);
908 
909 	if (fi->minspace)
910 	    height = ascent + descent;
911 	else
912 	{
913 	    vector.x = 0;
914 	    vector.y = face->size->metrics.height;
915 	    FT_Vector_Transform (&vector, &fi->matrix);
916 	    height = (int)(vector.y >> 6);
917 	}
918     }
919     else
920     {
921 	descent = -(int)(face->size->metrics.descender >> 6);
922 	ascent = (int)(face->size->metrics.ascender >> 6);
923 	if (fi->minspace)
924 	    height = ascent + descent;
925 	else
926 	    height = (int)(face->size->metrics.height >> 6);
927     }
928     font->public.ascent = ascent;
929     font->public.descent = descent;
930     font->public.height = height;
931 
932     if (fi->char_width)
933 	font->public.max_advance_width = fi->char_width;
934     else
935     {
936 	if (fi->transform)
937 	{
938 	    FT_Vector	vector;
939 	    vector.x = face->size->metrics.max_advance;
940 	    vector.y = 0;
941 	    FT_Vector_Transform (&vector, &fi->matrix);
942 	    font->public.max_advance_width = (int)(vector.x >> 6);
943 	}
944 	else
945 	    font->public.max_advance_width = (int)(face->size->metrics.max_advance >> 6);
946     }
947     font->public.charset = charset;
948     font->public.pattern = pattern;
949 
950     /*
951      * Management fields
952      */
953     font->ref = 1;
954 
955     font->next = info->fonts;
956     info->fonts = &font->public;
957 
958     font->hash_next = *bucket;
959     *bucket = &font->public;
960 
961     /*
962      * Copy the info over
963      */
964     font->info = *fi;
965     /*
966      * reset the antialias field.  It can't
967      * be set correctly until the font is opened,
968      * which doesn't happen in XftFontInfoFill
969      */
970     font->info.antialias = antialias;
971     /*
972      * bump XftFile reference count
973      */
974     font->info.file->ref++;
975 
976     /*
977      * Per glyph information
978      */
979     font->glyphs = (XftGlyph **) (font + 1);
980     memset (font->glyphs, '\0', (size_t)num_glyphs * sizeof (XftGlyph *));
981     font->num_glyphs = num_glyphs;
982     /*
983      * Unicode hash table information
984      */
985     font->hash_table = (XftUcsHash *) (font->glyphs + font->num_glyphs);
986     for (i = 0; i < hash_value; i++)
987     {
988 	font->hash_table[i].ucs4 = ((FcChar32) ~0);
989 	font->hash_table[i].glyph = 0;
990     }
991     font->hash_value = (int)hash_value;
992     font->rehash_value = (int)rehash_value;
993     /*
994      * X specific fields
995      */
996     font->glyphset = 0;
997     font->format = format;
998 
999     /*
1000      * Glyph memory management fields
1001      */
1002     font->glyph_memory = 0;
1003     font->max_glyph_memory = (unsigned long)max_glyph_memory;
1004     font->use_free_glyphs = info->use_free_glyphs;
1005 
1006     _XftUnlockFile (fi->file);
1007 
1008     return &font->public;
1009 
1010 bail2:
1011     FcCharSetDestroy (charset);
1012 bail1:
1013     _XftUnlockFile (fi->file);
1014 bail0:
1015     return NULL;
1016 }
1017 
1018 _X_EXPORT XftFont *
XftFontOpenPattern(Display * dpy,FcPattern * pattern)1019 XftFontOpenPattern (Display *dpy, FcPattern *pattern)
1020 {
1021     XftFontInfo	    info;
1022     XftFont	    *font;
1023 
1024     if (!XftFontInfoFill (dpy, pattern, &info))
1025 	return NULL;
1026 
1027     font = XftFontOpenInfo (dpy, pattern, &info);
1028     XftFontInfoEmpty (dpy, &info);
1029     return font;
1030 }
1031 
1032 _X_EXPORT XftFont *
XftFontCopy(Display * dpy,XftFont * public)1033 XftFontCopy (Display *dpy, XftFont *public)
1034 {
1035     XftFontInt	    *font = (XftFontInt *) public;
1036 
1037     font->ref++;
1038     return public;
1039 }
1040 
1041 static void
XftFontDestroy(Display * dpy,XftFont * public)1042 XftFontDestroy (Display *dpy, XftFont *public)
1043 {
1044     XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
1045     XftFontInt	    *font = (XftFontInt *) public;
1046     int		    i;
1047 
1048     /* note reduction in memory use */
1049     if (info)
1050 	info->glyph_memory -= font->glyph_memory;
1051     /* Clean up the info */
1052     XftFontInfoEmpty (dpy, &font->info);
1053     /* Free the glyphset */
1054     if (font->glyphset)
1055 	XRenderFreeGlyphSet (dpy, font->glyphset);
1056     /* Free the glyphs */
1057     for (i = 0; i < font->num_glyphs; i++)
1058     {
1059 	XftGlyph	*xftg = font->glyphs[i];
1060 	if (xftg)
1061 	{
1062 	    if (xftg->bitmap)
1063 		free (xftg->bitmap);
1064 	    free (xftg);
1065 	}
1066     }
1067 
1068     /* Free the pattern and the charset */
1069     FcPatternDestroy (font->public.pattern);
1070     FcCharSetDestroy (font->public.charset);
1071 
1072     /* Finally, free the font structure */
1073     XftMemFree (XFT_MEM_FONT, (sizeof (XftFontInt) +
1074 		(size_t)font->num_glyphs * sizeof (XftGlyph *) +
1075 		(size_t)font->hash_value * sizeof (XftUcsHash)));
1076     free (font);
1077 }
1078 
1079 static XftFont *
XftFontFindNthUnref(XftDisplayInfo * info,int n)1080 XftFontFindNthUnref (XftDisplayInfo *info, int n)
1081 {
1082     XftFont	*public;
1083     XftFontInt	*font;
1084 
1085     for (public = info->fonts; public; public = font->next)
1086     {
1087 	font = (XftFontInt*) public;
1088 	if (!font->ref && !n--)
1089 	    break;
1090     }
1091     return public;
1092 }
1093 
1094 _X_HIDDEN void
XftFontManageMemory(Display * dpy)1095 XftFontManageMemory (Display *dpy)
1096 {
1097     XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
1098     XftFont	    **prev;
1099     XftFont	    *public;
1100     XftFontInt	    *font;
1101 
1102     if (!info)
1103 	return;
1104     while (info->num_unref_fonts > info->max_unref_fonts)
1105     {
1106 	public = XftFontFindNthUnref (info, rand() % info->num_unref_fonts);
1107 	font = (XftFontInt *) public;
1108 
1109 	if (XftDebug () & XFT_DBG_CACHE)
1110 	    printf ("freeing unreferenced font %s/%d size %dx%d\n",
1111 		    font->info.file->file, font->info.file->id,
1112 		    (int) font->info.xsize >> 6, (int) font->info.ysize >> 6);
1113 
1114 	/* Unhook from display list */
1115 	for (prev = &info->fonts; *prev; prev = &(*(XftFontInt **) prev)->next)
1116 	{
1117 	    if (*prev == public)
1118 	    {
1119 		*prev = font->next;
1120 		break;
1121 	    }
1122 	}
1123 	/* Unhook from hash list */
1124 	for (prev = &info->fontHash[font->info.hash % XFT_NUM_FONT_HASH];
1125 	     *prev;
1126 	     prev = &(*(XftFontInt **) prev)->hash_next)
1127 	{
1128 	    if (*prev == public)
1129 	    {
1130 		*prev = font->hash_next;
1131 		break;
1132 	    }
1133 	}
1134 	/* Destroy the font */
1135 	XftFontDestroy (dpy, public);
1136 	--info->num_unref_fonts;
1137     }
1138 }
1139 
1140 _X_EXPORT void
XftFontClose(Display * dpy,XftFont * public)1141 XftFontClose (Display *dpy, XftFont *public)
1142 {
1143     XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
1144     XftFontInt	    *font = (XftFontInt *) public;
1145 
1146     if (--font->ref != 0)
1147 	return;
1148 
1149     if (info)
1150     {
1151 	++info->num_unref_fonts;
1152 	XftFontManageMemory (dpy);
1153     }
1154     else
1155     {
1156 	XftFontDestroy (dpy, public);
1157     }
1158 }
1159 
1160 _X_EXPORT FcBool
XftInitFtLibrary(void)1161 XftInitFtLibrary (void)
1162 {
1163     if (_XftFTlibrary)
1164 	return FcTrue;
1165     if (FT_Init_FreeType (&_XftFTlibrary))
1166 	return FcFalse;
1167     return FcTrue;
1168 }
1169