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