1 /********************************/
2 /*                               */
3 /*  Xft - client-side X11 fonts  */
4 /*                               */
5 /*********************************/
6 
7 
8 /*
9 
10 USAGE
11 -----
12 To use specific Xft fonts, set Prima font names in your X resource
13 database in fontconfig formats - for example, Palatino-12. Consult
14 'man fontconfig' for the syntax, but be aware that Prima uses only
15 size, weight, and name fields.
16 
17 IMPLEMENTATION NOTES
18 --------------------
19 
20 This implementations doesn't work with non-scalable Xft fonts,
21 since their rasterization capabilities are currently ( June 2003) very limited -
22 no scaling and no rotation. Plus, the selection of the non-scalable fonts is
23 a one big something, and I believe that one in apc_font.c is enough.
24 
25 The following Xft/fontconfig problems, if fixed in th next versions, need to be
26 taken into the consideration:
27 - no font/glyph data - internal leading, underscore size/position,
28 	no strikeout size/position, average width.
29 - no raster operations
30 - no glyph reference point shift
31 - no client-side access to glyph bitmaps
32 - no on-the-fly antialiasing toggle
33 - no text background painting ( only rectangles )
34 - no text strikeout / underline drawing routines
35 - produces xlib failures for large polygons - answered to be a Xrender bug;
36 	the X error handler catches this now.
37 
38 TO DO
39 -----
40 - The full set of raster operations - not supported by xft ( stupid )
41 - apc_show_message - probably never will be implemented though
42 - Investigate if ICONV can be replaced by native perl's ENCODE interface
43 - Under some circumstances Xft decides to put antialiasing aside, for
44 	example, on the paletted displays. Check, if some heuristics that would
45 	govern whether Xft is to be used there, are needed.
46 
47 */
48 
49 #include "unix/guts.h"
50 
51 #ifdef USE_XFT
52 
53 #ifdef HAVE_ICONV_H
54 #include <iconv.h>
55 #endif
56 
57 #ifdef WITH_HARFBUZZ
58 #include <harfbuzz/hb.h>
59 #include <harfbuzz/hb-ft.h>
60 #endif
61 
62 /* fontconfig version < 2.2.0 */
63 #ifndef FC_WEIGHT_NORMAL
64 #define FC_WEIGHT_NORMAL 80
65 #endif
66 #ifndef FC_WEIGHT_THIN
67 #define FC_WEIGHT_THIN 0
68 #endif
69 #ifndef FC_WIDTH
70 #define FC_WIDTH "width"
71 #endif
72 
73 static int xft_debug_indent = 0;
74 #define XFTdebug if (pguts->debug & DEBUG_FONTS) xft_debug
75 
76 typedef struct {
77 	char      *name;
78 	FcCharSet *fcs;
79 	int        glyphs;
80 	Bool       enabled;
81 	uint32_t   map[128];   /* maps characters 128-255 into unicode */
82 } CharSetInfo;
83 
84 static CharSetInfo std_charsets[] = {
85 	{ "iso8859-1",  NULL, 0, 1 }
86 #ifdef HAVE_ICONV_H
87 	,
88 	{ "iso8859-2",  NULL, 0, 0 },
89 	{ "iso8859-3",  NULL, 0, 0 },
90 	{ "iso8859-4",  NULL, 0, 0 },
91 	{ "iso8859-5",  NULL, 0, 0 },
92 	{ "iso8859-7",  NULL, 0, 0 },
93 	{ "iso8859-8",  NULL, 0, 0 },
94 	{ "iso8859-9",  NULL, 0, 0 },
95 	{ "iso8859-10", NULL, 0, 0 },
96 	{ "iso8859-13", NULL, 0, 0 },
97 	{ "iso8859-14", NULL, 0, 0 },
98 	{ "iso8859-15", NULL, 0, 0 },
99 	{ "koi8-r",     NULL, 0, 0 }  /* this is special - change the constant
100 					KOI8_INDEX as well when updating
101 					the table */
102 /* You are welcome to add more 8-bit charsets here - just keep in mind
103 	that each encoding requires iconv() to load a file */
104 #endif
105 };
106 
107 static CharSetInfo fontspecific_charset = { "fontspecific", NULL, 0, 1 };
108 static CharSetInfo utf8_charset         = { "iso10646-1",   NULL, 0, 1 };
109 
110 #define KOI8_INDEX 12
111 #define MAX_CHARSET (sizeof(std_charsets)/sizeof(CharSetInfo))
112 #define STD_CHARSETS MAX_CHARSET
113 #define EXTRA_CHARSETS 1
114 #define ALL_CHARSETS (STD_CHARSETS+EXTRA_CHARSETS)
115 #define MAX_GLYPH_SIZE (guts.limits.request_length / 256)
116 
117 #define ROUND_DIRECTION 1000.0
118 #define IS_ZERO(a)  ((int)(a*ROUND_DIRECTION)==0)
119 #define ROUGHLY(a) (((int)(a*ROUND_DIRECTION))/ROUND_DIRECTION)
120 
121 
122 static PHash encodings    = NULL;
123 static PHash mono_fonts   = NULL; /* family->mono font mapping */
124 static PHash prop_fonts   = NULL; /* family->proportional font mapping */
125 static PHash mismatch     = NULL; /* fonts not present in xft base */
126 static PHash myfont_cache = NULL; /* fonts loaded temporarily */
127 static char  fontspecific[] = "fontspecific";
128 static char  utf8_encoding[] = "iso10646-1";
129 static CharSetInfo * locale = NULL;
130 
131 typedef struct {
132 	Font font;
133 	XftFont *orig, *xft_font;
134 	XftFont *orig_base, *xft_base_font;
135 	uint32_t fid;
136 	uint16_t *fonts;
137 } FontContext;
138 
139 static void
font_context_init(FontContext * fc,Font * font,uint16_t * fonts,XftFont * orig,XftFont * base)140 font_context_init( FontContext * fc, Font * font, uint16_t * fonts, XftFont * orig, XftFont * base)
141 {
142 	bzero(fc, sizeof(FontContext));
143 	fc->font      = *font;
144 	fc->orig      = fc->xft_font = orig;
145 	fc->orig_base = fc->xft_base_font = base;
146 	fc->fid       = 0;
147 	fc->fonts     = fonts;
148 }
149 
150 static void
font_context_next(FontContext * fc)151 font_context_next( FontContext * fc )
152 {
153 	Font *_src, src, dst;
154 	uint16_t nfid;
155 
156 	if ( !fc-> fonts ) return;
157 
158 	nfid = *(fc->fonts++);
159 	if ( nfid == fc-> fid ) return;
160 
161 	fc->fid = nfid;
162 	if ( nfid == 0 ) {
163 		fc->xft_font = fc->orig;
164 		fc->xft_base_font = fc->orig_base;
165 		return;
166 	}
167 
168 	if ( !( _src = prima_font_mapper_get_font(nfid)))
169 		return;
170 
171 	src = *_src;
172 	dst = fc->font;
173 #define CP(x) src.x = dst.x; src.undef.x = 0;
174 	CP(size)
175 	CP(direction)
176 #undef CP
177 
178 	prima_xft_font_pick( NULL_HANDLE, &src, &dst, NULL, &fc->xft_font);
179 	if ( !fc->orig_base )
180 		return;
181 
182 	if ( IS_ZERO(fc->font.direction))
183 		fc-> xft_base_font = fc->xft_font;
184 	else {
185 		dst.direction = 0;
186 		prima_xft_font_pick( NULL_HANDLE, &dst, &dst, NULL, &fc->xft_base_font);
187 	}
188 }
189 
190 
191 static void
xft_debug(const char * format,...)192 xft_debug( const char *format, ...)
193 {
194 	int i;
195 	va_list args;
196 	va_start( args, format);
197 	fprintf( stderr, "xft: ");
198 	for ( i = 0; i < xft_debug_indent * 3; i++) fprintf( stderr, " ");
199 	vfprintf( stderr, format, args);
200 	fprintf( stderr, "\n");
201 	va_end( args);
202 }
203 
204 void
prima_xft_init(void)205 prima_xft_init(void)
206 {
207 	int i;
208 	FcCharSet * fcs_ascii;
209 #ifdef HAVE_ICONV_H
210 	iconv_t ii;
211 	unsigned char in[128], *iptr;
212 	char ucs4[12];
213 	size_t ibl, obl;
214 	uint32_t *optr;
215 	int j;
216 #endif
217 
218 	if ( !apc_fetch_resource( "Prima", "", "UseXFT", "usexft",
219 				NULL_HANDLE, frUnix_int, &guts. use_xft))
220 		guts. use_xft = 1;
221 	if ( guts. use_xft) {
222 		if ( !XftInit(0)) guts. use_xft = 0;
223 	}
224 	/* After this point guts.use_xft must never be altered */
225 	if ( !guts. use_xft) return;
226 	XFTdebug("XFT ok");
227 
228 	fcs_ascii = FcCharSetCreate();
229 	for ( i = 32; i < 127; i++)  FcCharSetAddChar( fcs_ascii, i);
230 
231 
232 	std_charsets[0]. fcs = FcCharSetUnion( fcs_ascii, fcs_ascii);
233 	for ( i = 161; i < 255; i++) FcCharSetAddChar( std_charsets[0]. fcs, i);
234 	for ( i = 128; i < 255; i++) std_charsets[0]. map[i - 128] = i;
235 	std_charsets[0]. glyphs = ( 127 - 32) + ( 255 - 161);
236 
237 #ifdef HAVE_ICONV_H
238 	sprintf( ucs4, "UCS-4%cE", (guts.machine_byte_order == LSBFirst) ? 'L' : 'B');
239 	for ( i = 1; i < STD_CHARSETS; i++) {
240 		memset( std_charsets[i]. map, 0, sizeof(std_charsets[i]. map));
241 
242 		ii = iconv_open(ucs4, std_charsets[i]. name);
243 		if ( ii == (iconv_t)(-1)) continue;
244 
245 		std_charsets[i]. fcs = FcCharSetUnion( fcs_ascii, fcs_ascii);
246 		for ( j = 0; j < 128; j++) in[j] = j + 128;
247 		iptr = in;
248 		optr = std_charsets[i]. map;
249 		ibl = 128;
250 		obl = 128 * sizeof( uint32_t);
251 		while ( 1 ) {
252 			int ret = iconv( ii, ( char **) &iptr, &ibl, ( char **) &optr, &obl);
253 			if ( ret < 0 && errno == EILSEQ) {
254 				iptr++;
255 				optr++;
256 				ibl--;
257 				obl -= sizeof(uint32_t);
258 				continue;
259 			}
260 			break;
261 		}
262 		iconv_close(ii);
263 
264 		optr = std_charsets[i]. map - 128;
265 		std_charsets[i]. glyphs = 127 - 32;
266 		for ( j = (( i == KOI8_INDEX) ? 191 : 161); j < 256; j++)
267 			/* koi8 hack - 161-190 are pseudo-graphic symbols, not really characters,
268 				so don't use them for font matching by a charset */
269 			if ( optr[j]) {
270 				FcCharSetAddChar( std_charsets[i]. fcs, optr[j]);
271 				std_charsets[i]. glyphs++;
272 			}
273 		if ( std_charsets[i]. glyphs > 127 - 32)
274 			std_charsets[i]. enabled = true;
275 	}
276 #endif
277 
278 	mismatch     = hash_create();
279 	mono_fonts   = hash_create();
280 	prop_fonts   = hash_create();
281 	encodings    = hash_create();
282 	myfont_cache = hash_create();
283 	for ( i = 0; i < STD_CHARSETS; i++) {
284 		int length = 0;
285 		char upcase[256], *dest = upcase, *src = std_charsets[i].name;
286 		if ( !std_charsets[i]. enabled) continue;
287 		while ( *src) {
288 			*dest++ = toupper(*src++);
289 			length++;
290 		}
291 		hash_store( encodings, upcase, length, (void*) (std_charsets + i));
292 		hash_store( encodings, std_charsets[i]. name, length, (void*) (std_charsets + i));
293 	}
294 
295 	fontspecific_charset. fcs = FcCharSetCreate();
296 	for ( i = 128; i < 256; i++) fontspecific_charset. map[i - 128] = i;
297 	hash_store( encodings, fontspecific, strlen(fontspecific), (void*) &fontspecific_charset);
298 
299 	utf8_charset. fcs = FcCharSetCreate();
300 	for ( i = 128; i < 256; i++) utf8_charset. map[i - 128] = i;
301 	hash_store( encodings, utf8_encoding, strlen(utf8_encoding), (void*) &utf8_charset);
302 
303 	locale = hash_fetch( encodings, guts. locale, strlen( guts.locale));
304 	if ( !locale) locale = std_charsets;
305 	FcCharSetDestroy( fcs_ascii);
306 }
307 
308 static Bool
remove_myfonts(void * f,int keyLen,void * key,void * dummy)309 remove_myfonts( void * f, int keyLen, void * key, void * dummy)
310 {
311 	unlink((char*) key);
312 	return false;
313 }
314 
315 void
prima_xft_done(void)316 prima_xft_done(void)
317 {
318 	int i;
319 	if ( !guts. use_xft) return;
320 	for ( i = 0; i < STD_CHARSETS; i++)
321 		if ( std_charsets[i]. fcs)
322 			FcCharSetDestroy( std_charsets[i]. fcs);
323 	FcCharSetDestroy( fontspecific_charset. fcs);
324 	FcCharSetDestroy( utf8_charset. fcs);
325 	hash_destroy( encodings, false);
326 	hash_destroy( mismatch, false);
327 	hash_destroy( prop_fonts, true);
328 	hash_destroy( mono_fonts, true);
329 
330 	hash_first_that( myfont_cache, (void*)remove_myfonts, NULL, NULL, NULL);
331 	hash_destroy( myfont_cache, false);
332 	myfont_cache = NULL;
333 
334 }
335 
336 static Bool
utf8_flag_strncpy(char * dst,const char * src,unsigned int maxlen)337 utf8_flag_strncpy( char * dst, const char * src, unsigned int maxlen)
338 {
339 	Bool is_utf8 = false;
340 	while ( maxlen-- && *src) {
341 		if ( *((unsigned char*)src) > 0x7f)
342 			is_utf8 = true;
343 		*(dst++) = *(src++);
344 	}
345 	*dst = 0;
346 	return is_utf8;
347 }
348 
349 static void
fcpattern2fontnames(FcPattern * pattern,Font * font)350 fcpattern2fontnames( FcPattern * pattern, Font * font)
351 {
352 	FcChar8 * s;
353 
354 	if ( FcPatternGetString( pattern, FC_FAMILY, 0, &s) == FcResultMatch)
355 		font-> is_utf8.name = utf8_flag_strncpy( font-> name, (char*)s, 255);
356 	if ( FcPatternGetString( pattern, FC_FOUNDRY, 0, &s) == FcResultMatch)
357 		font-> is_utf8.family = utf8_flag_strncpy( font-> family, (char*)s, 255);
358 
359 	/* fake family */
360 	if (
361 		( strcmp(font->family, "") == 0) ||
362 		( strcmp(font->family, "unknown") == 0)
363 	) {
364 		char * name   = font->name;
365 		char * family = font->family;
366 		while (*name && *name != ' ') {
367 			*family++ = (*name < 127 ) ? tolower(*name) : *name;
368 			name++;
369 		}
370 		*family = 0;
371 	}
372 }
373 
374 static void
fcpattern2font(FcPattern * pattern,PFont font)375 fcpattern2font( FcPattern * pattern, PFont font)
376 {
377 	int i, j;
378 	double d = 1.0;
379 	FcCharSet *c = NULL;
380 
381 	/* FcPatternPrint( pattern); */
382 	fcpattern2fontnames(pattern, font);
383 
384 	font-> style = 0;
385 	font-> undef. style = 0;
386 	if ( FcPatternGetInteger( pattern, FC_SLANT, 0, &i) == FcResultMatch)
387 		if ( i == FC_SLANT_ITALIC || i == FC_SLANT_OBLIQUE)
388 			font-> style |= fsItalic;
389 	if ( FcPatternGetInteger( pattern, FC_WEIGHT, 0, &i) == FcResultMatch) {
390 		if ( i <= FC_WEIGHT_LIGHT)
391 			font-> style |= fsThin;
392 		else if ( i >= FC_WEIGHT_BOLD)
393 			font-> style |= fsBold;
394 		font-> weight = i * fwUltraBold / FC_WEIGHT_ULTRABOLD;
395 	}
396 
397 	font-> xDeviceRes = guts. resolution. x;
398 	font-> yDeviceRes = guts. resolution. y;
399 	if ( FcPatternGetDouble( pattern, FC_DPI, 0, &d) == FcResultMatch)
400 		font-> yDeviceRes = d + 0.5;
401 	if ( FcPatternGetDouble( pattern, FC_ASPECT, 0, &d) == FcResultMatch)
402 		font-> xDeviceRes = font-> yDeviceRes * d;
403 
404 	if ( FcPatternGetInteger( pattern, FC_SPACING, 0, &i) == FcResultMatch) {
405 		font-> pitch = (( i == FC_PROPORTIONAL) ? fpVariable : fpFixed);
406 		font-> undef. pitch = 0;
407 	}
408 
409 	if ( FcPatternGetDouble( pattern, FC_PIXEL_SIZE, 0, &d) == FcResultMatch) {
410 		font->height = d + 0.5;
411 		font-> undef. height = 0;
412 		XFTdebug("height factor read:%d (%g)", font-> height, d);
413 	}
414 
415 	font-> width = 100; /* warning, FC_WIDTH does not reflect FC_MATRIX scale changes */
416 	if ( FcPatternGetDouble( pattern, FC_WIDTH, 0, &d) == FcResultMatch) {
417 		font->width = d + 0.5;
418 		XFTdebug("width factor read:%d (%g)", font-> width, d);
419 	}
420 	font-> width = ( font-> width * font-> height) / 100.0 + .5;
421 	font->undef. width = 0;
422 
423 	if ( FcPatternGetDouble( pattern, FC_SIZE, 0, &d) == FcResultMatch) {
424 		font->size = d + 0.5;
425 		font->undef. size = 0;
426 		XFTdebug("size factor read:%d (%g)", font-> size, d);
427 	} else if (!font-> undef.height && font->yDeviceRes != 0) {
428 		font-> size = font-> height * 72.27 / font-> yDeviceRes + .5;
429 		font-> undef. size = 0;
430 		XFTdebug("size calculated:%d", font-> size);
431 	} else {
432 		XFTdebug("size unknown");
433 	}
434 
435 	/* fvBitmap is 0, fvOutline is 1 */
436 	font-> vector = fvOutline;
437 	if ( FcPatternGetBool( pattern, FC_SCALABLE, 0, &font-> vector) == FcResultMatch)
438 		font-> undef. vector = 0;
439 
440 	font-> firstChar = 32; font-> lastChar = 255;
441 	font-> breakChar = 32; font-> defaultChar = 32;
442 	if (( FcPatternGetCharSet( pattern, FC_CHARSET, 0, &c) == FcResultMatch) && c) {
443 		FcChar32 ucs4, next, map[FC_CHARSET_MAP_SIZE];
444 		if (( ucs4 = FcCharSetFirstPage( c, map, &next)) != FC_CHARSET_DONE) {
445 			for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
446 				if ( map[i] ) {
447 					for (j = 0; j < 32; j++)
448 						if (map[i] & (1 << j)) {
449 							FcChar32 u = ucs4 + i * 32 + j;
450 							font-> firstChar = u;
451 							if ( font-> breakChar   < u) font-> breakChar   = u;
452 							if ( font-> defaultChar < u) font-> defaultChar = u;
453 							goto STOP_1;
454 						}
455 				}
456 STOP_1:;
457 			while ( next != FC_CHARSET_DONE)
458 				ucs4 = FcCharSetNextPage (c, map, &next);
459 			for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
460 				if ( map[i] ) {
461 					for (j = 31; j >= 0; j--)
462 						if (map[i] & (1 << j)) {
463 							FcChar32 u = ucs4 + i * 32 + j;
464 							font-> lastChar = u;
465 							if ( font-> breakChar   > u) font-> breakChar   = u;
466 							if ( font-> defaultChar > u) font-> defaultChar = u;
467 							goto STOP_2;
468 						}
469 				}
470 STOP_2:;
471 		}
472 	}
473 
474 	/* XXX other details? */
475 	font-> descent = font-> height / 4;
476 	font-> ascent  = font-> height - font-> descent;
477 	font-> maximalWidth = font-> width;
478 	font-> internalLeading = 0;
479 	font-> externalLeading = 0;
480 }
481 
482 static void
xft_build_font_key(PFontKey key,PFont f,Bool bySize)483 xft_build_font_key( PFontKey key, PFont f, Bool bySize)
484 {
485 	bzero( key, sizeof( FontKey));
486 	key-> height = bySize ? -f-> size : f-> height;
487 	key-> width  = f-> width;
488 	key-> style  = f-> style & ~(fsUnderlined|fsOutline|fsStruckOut) & fsMask;
489 	key-> pitch  = f-> pitch & fpMask;
490 	key-> vector = (f-> vector == fvBitmap) ? 0 : 1;
491 	key-> direction = ROUGHLY(f-> direction);
492 	strcpy( key-> name, f-> name);
493 }
494 
495 static XftFont *
try_size(Handle self,Font f,double size)496 try_size( Handle self, Font f, double size)
497 {
498 	XftFont * xft = NULL;
499 	bzero( &f.undef, sizeof(f.undef));
500 	f. undef. height = f. undef. width = 1;
501 	f. size = size + 0.5;
502 	f. direction = 0.0;
503 	xft_debug_indent++;
504 	prima_xft_font_pick( self, &f, &f, &size, &xft);
505 	xft_debug_indent--;
506 	return xft;
507 }
508 
509 /* find a most similar monospace/proportional font by name and family */
510 static char *
find_good_font_by_family(Font * f,int fc_spacing)511 find_good_font_by_family( Font * f, int fc_spacing )
512 {
513 	static Bool initialized = 0;
514 
515 	if ( !initialized ) {
516 		/* iterate over all monospace and proportional font, build family->name (i.e best default match) hash */
517 		int i;
518 		FcFontSet * s;
519 		FcPattern   *pat, **ppat;
520 		FcObjectSet *os;
521 
522 		initialized = 1;
523 
524 		pat = FcPatternCreate();
525 		FcPatternAddBool( pat, FC_SCALABLE, 1);
526 		os = FcObjectSetBuild( FC_FAMILY, FC_CHARSET, FC_ASPECT,
527 			FC_SLANT, FC_WEIGHT, FC_SIZE, FC_PIXEL_SIZE, FC_SPACING,
528 			FC_FOUNDRY, FC_SCALABLE, FC_DPI,
529 			(void*) 0);
530 		s = FcFontList( 0, pat, os);
531 		FcObjectSetDestroy( os);
532 		FcPatternDestroy( pat);
533 		if ( !s) return NULL;
534 
535 		ppat = s-> fonts;
536 		for ( i = 0; i < s->nfont; i++, ppat++) {
537 			Font f;
538 			int spacing = FC_PROPORTIONAL, slant, len, weight;
539 			FcBool scalable;
540 			PHash font_hash;
541 
542 			/* only regular fonts */
543 			if (
544 				( FcPatternGetInteger( *ppat, FC_SLANT, 0, &slant) != FcResultMatch) ||
545 				( slant == FC_SLANT_ITALIC || slant == FC_SLANT_OBLIQUE)
546 			)
547 				continue;
548 			if (
549 				( FcPatternGetInteger( *ppat, FC_WEIGHT, 0, &weight) != FcResultMatch) ||
550 				( weight <= FC_WEIGHT_LIGHT || weight >= FC_WEIGHT_BOLD)
551 			)
552 				continue;
553 			if (
554 				( FcPatternGetBool( *ppat, FC_SCALABLE, 0, &scalable) != FcResultMatch) ||
555 				( scalable == 0 )
556 			)
557 				continue;
558 
559 			fcpattern2fontnames( *ppat, &f);
560 			len = strlen(f.family);
561 
562 			/* sort fonts by family and spacing */
563 			font_hash = (
564 				( FcPatternGetInteger( *ppat, FC_SPACING, 0, &spacing) == FcResultMatch) &&
565 				( spacing == FC_MONO )
566 			) ?
567 				mono_fonts : prop_fonts;
568 			if ( hash_fetch( font_hash, f.family, len))
569 				continue;
570 
571 			if ( spacing == FC_MONO ) {
572 				char * p;
573 				#define MONO_TAG " Mono"
574 				if (( p = strcasestr(f.name, MONO_TAG)) == NULL )
575 					continue;
576 				p += strlen(MONO_TAG);
577 				if ( *p != ' ' && *p != 0 )
578 					continue;
579 				#undef MONO_TAG
580 			}
581 
582 			hash_store( font_hash, f.family, len, duplicate_string(f.name));
583 		}
584 		FcFontSetDestroy(s);
585 	}
586 	/* initialized ok */
587 	XFTdebug("trying to find %s pitch replacement for %s/%s",
588 		(fc_spacing == FC_MONO ) ? "fixed" : "variable",
589 		f->name, f->family);
590 
591 	/* try to find same family and same 1st word in font name */
592 	{
593 		char *c, *w, word1[255], word2[255];
594 		PHash font_hash = (fc_spacing == FC_MONO) ? mono_fonts : prop_fonts;
595 		c = hash_fetch( font_hash, f->family, strlen(f->family));
596 		if ( !c ) {
597 			XFTdebug("no default font for that family");
598 			return NULL;
599 		}
600 		if ( strcmp( c, f->name) == 0) {
601 			XFTdebug("same font");
602 			return NULL; /* same font */
603 		}
604 
605 		strcpy( word1, c);
606 		strcpy( word2, f->name);
607 		if (( w = strchr( word1, ' '))) *w = 0;
608 		if (( w = strchr( word2, ' '))) *w = 0;
609 		if ( strcmp( word1, word2 ) != 0 ) {
610 			XFTdebug("default font %s doesn't look similar", c);
611 			return NULL;
612 		}
613 		XFTdebug("replaced with %s", c);
614 
615 		return c;
616 	}
617 }
618 
619 static void
xft_store_font(Font * k,Font * v,Bool by_size,XftFont * xft,XftFont * xft_base)620 xft_store_font(Font * k, Font * v, Bool by_size, XftFont * xft, XftFont * xft_base)
621 {
622 	FontKey key;
623 	PCachedFont kf;
624 	xft_build_font_key( &key, k, by_size);
625 	if ( !hash_fetch( guts. font_hash, &key, sizeof(FontKey))) {
626 		if (( kf = malloc( sizeof( CachedFont)))) {
627 			bzero( kf, sizeof( CachedFont));
628 			memcpy( &kf-> font, v, sizeof( Font));
629 			kf-> font. style &= ~(fsUnderlined|fsOutline|fsStruckOut) & fsMask;
630 			kf-> xft      = xft;
631 			kf-> xft_base = xft_base;
632 			hash_store( guts. font_hash, &key, sizeof( FontKey), kf);
633 			XFTdebug("store %x(%x):%dx%d.%s.%s.%s^%g", xft, xft_base, key.height, key.width, _F_DEBUG_STYLE(key.style), _F_DEBUG_PITCH(key.pitch), key.name, ROUGHLY(key.direction));
634 		}
635 	}
636 }
637 
638 static int force_xft_monospace_emulation = 0;
639 static int try_xft_monospace_emulation_by_name = 0;
640 
641 /* Bug #128146: libXft might use another shared libfontconfig library, as
642  * observed on a badly configured macos, and thus FcPattern* returned by it
643  * can crash everything */
644 static FcPattern *
my_XftFontMatch(Display * dpy,int screen,_Xconst FcPattern * pattern,FcResult * result)645 my_XftFontMatch(Display        *dpy,
646               int               screen,
647               _Xconst FcPattern *pattern,
648               FcResult          *result)
649 {
650     FcPattern   *new;
651     FcPattern   *match;
652     new = FcPatternDuplicate (pattern);
653     if (!new)
654         return NULL;
655     FcConfigSubstitute (NULL, new, FcMatchPattern);
656     XftDefaultSubstitute (dpy, screen, new);
657     match = FcFontMatch (NULL, new, result);
658     FcPatternDestroy (new);
659     return match;
660 }
661 
662 Bool
prima_xft_font_pick(Handle self,Font * source,Font * dest,double * size,XftFont ** xft_result)663 prima_xft_font_pick( Handle self, Font * source, Font * dest, double * size, XftFont ** xft_result)
664 {
665 	FcPattern *request, *match;
666 	FcResult res = FcResultNoMatch;
667 	Font requested_font, loaded_font;
668 	Bool by_size;
669 	CharSetInfo * csi;
670 	XftFont * xf;
671 	FontKey key;
672 	PCachedFont kf, kf_base = NULL;
673 	int i, base_width = 1, exact_pixel_size = 0, cache_results = 1;
674 	double pixel_size;
675 
676 	if ( !guts. use_xft) return false;
677 
678 	requested_font = *dest;
679 	by_size = Drawable_font_add( self, source, &requested_font);
680 	pixel_size = requested_font. height;
681 
682 	if ( guts. xft_disable_large_fonts > 0) {
683 		/* xft is unable to deal with large polygon requests.
684 			we must cut the large fonts here, before Xlib croaks */
685 		if (
686 			( by_size && ( requested_font. size >= MAX_GLYPH_SIZE)) ||
687 			(!by_size && ( requested_font. height >= MAX_GLYPH_SIZE / 72.27 * guts. resolution. y))
688 		)
689 			return false;
690 	}
691 
692 	/* we don't want to cache fractional sizes because these can lead to incoherent results
693 		depending on whether we match a particular height by size or by height */
694 	if ( by_size && size && *size * 100 != requested_font.size * 100 ) {
695 		cache_results = 0;
696 		XFTdebug("not caching results because size %g is fractional", *size);
697 	}
698 
699 	/* see if the font is not present in xft - the hashed negative matches
700 			are stored with width=0, as the width alterations are derived */
701 	xft_build_font_key( &key, &requested_font, by_size);
702 	XFTdebug("want %dx%d.%s.%s.%s/%s^%g.%d", key.height, key. width, _F_DEBUG_STYLE(key.style), _F_DEBUG_PITCH(key.pitch), key.name, requested_font.encoding, ROUGHLY(requested_font.direction), requested_font.vector);
703 
704 	key. width = 0;
705 	if ( hash_fetch( mismatch, &key, sizeof( FontKey))) {
706 		XFTdebug("refuse");
707 		return false;
708 	}
709 	key. width = requested_font. width;
710 
711 	/* convert encoding */
712 	csi = ( CharSetInfo*) hash_fetch( encodings, requested_font. encoding, strlen( requested_font. encoding));
713 	if ( !csi) {
714 		/* xft has no such encoding, pass it back */
715 		if ( prima_core_font_encoding( requested_font. encoding) || !guts. xft_priority)
716 			return false;
717 		csi = locale;
718 	}
719 
720 	/* see if cached font exists */
721 	if (( kf = hash_fetch( guts. font_hash, &key, sizeof( FontKey)))) {
722 		*dest = kf-> font;
723 		strcpy( dest-> encoding, csi-> name);
724 		if ( requested_font. style & fsStruckOut) dest-> style |= fsStruckOut;
725 		if ( requested_font. style & fsUnderlined) dest-> style |= fsUnderlined;
726 		if ( xft_result ) *xft_result = kf-> xft;
727 		return true;
728 	}
729 	/* see if the non-xscaled font exists */
730 	if ( key. width != 0) {
731 		key. width = 0;
732 		if ( !( kf = hash_fetch( guts. font_hash, &key, sizeof( FontKey)))) {
733 			Font s = *source, d = *dest;
734 			s. width = d. width = 0;
735 			XFTdebug("try nonscaled font");
736 			xft_debug_indent++;
737 			prima_xft_font_pick( self, &s, &d, size, NULL);
738 			xft_debug_indent--;
739 		}
740 		if ( kf || ( kf = hash_fetch( guts. font_hash, &key, sizeof( FontKey)))) {
741 			base_width = kf-> font. width;
742 			if ( FcPatternGetDouble( kf-> xft-> pattern, FC_PIXEL_SIZE, 0, &pixel_size) == FcResultMatch) {
743 				exact_pixel_size = 1;
744 				XFTdebug("existing base font %x %dx0 suggests exact_pixel_size %g base_width %d", kf->xft, key.height, pixel_size, base_width);
745 			}
746 		} else { /* if fails, cancel x scaling and see if it failed due to banning */
747 			if ( hash_fetch( mismatch, &key, sizeof( FontKey))) return false;
748 			requested_font. width = 0;
749 		}
750 	}
751 	/* see if the non-rotated font exists */
752 	if ( !IS_ZERO(key. direction)) {
753 		key. direction = 0.0;
754 		key. width = requested_font. width;
755 		if ( !( kf_base = hash_fetch( guts. font_hash, &key, sizeof( FontKey)))) {
756 			Font s = *source, d = *dest;
757 			s. direction = d. direction = 0.0;
758 			XFTdebug("try nonrotated font");
759 			xft_debug_indent++;
760 			prima_xft_font_pick( self, &s, &d, size, NULL);
761 			xft_debug_indent--;
762 			/* if fails, cancel rotation and see if the base font is banned  */
763 			if ( !( kf_base = hash_fetch( guts. font_hash, &key, sizeof( FontKey))))
764 				requested_font. direction = 0.0;
765 		}
766 		if ( !IS_ZERO(requested_font. direction)) {
767 			/* as requested_font. height != FC_PIXEL_SIZE, read the correct request
768 				from the non-rotated font */
769 			if ( FcPatternGetDouble( kf_base-> xft-> pattern, FC_PIXEL_SIZE, 0, &pixel_size) == FcResultMatch) {
770 				XFTdebug("existing base font %x %dx%d dir=0 suggests exact_pixel_size %g", kf_base->xft, key.height, key.width, pixel_size);
771 				exact_pixel_size = 1;
772 			}
773 		}
774 	}
775 
776 	/* create FcPattern request */
777 	if ( !( request = FcPatternCreate())) return false;
778 	if ( strcmp( requested_font. name, "Default") != 0)
779 		FcPatternAddString( request, FC_FAMILY, ( FcChar8*) requested_font. name);
780 	if ( by_size) {
781 		if ( size) {
782 			FcPatternAddDouble( request, FC_SIZE, *size);
783 			XFTdebug("FC_SIZE = %.1f", *size);
784 		} else {
785 			FcPatternAddDouble( request, FC_SIZE, requested_font. size);
786 			XFTdebug("FC_SIZE = %d", requested_font. size);
787 		}
788 	} else {
789 		FcPatternAddDouble( request, FC_PIXEL_SIZE, pixel_size);
790 		XFTdebug("FC_PIXEL_SIZE = %g", pixel_size);
791 	}
792 	FcPatternAddInteger( request, FC_SPACING,
793 		(requested_font. pitch == fpFixed && force_xft_monospace_emulation) ? FC_MONO : FC_PROPORTIONAL);
794 
795 	FcPatternAddInteger( request, FC_SLANT, ( requested_font. style & fsItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
796 	FcPatternAddInteger( request, FC_WEIGHT,
797 		( requested_font. style & fsBold) ? FC_WEIGHT_BOLD :
798 		( requested_font. style & fsThin) ? FC_WEIGHT_THIN : FC_WEIGHT_NORMAL);
799 	FcPatternAddBool( request, FC_SCALABLE, requested_font. vector > fvBitmap );
800 	if ( !IS_ZERO(requested_font. direction) || requested_font. width != 0) {
801 		FcMatrix mat;
802 		FcMatrixInit(&mat);
803 		if ( requested_font. width != 0) {
804 			FcMatrixScale( &mat, ( double) requested_font. width / base_width, 1);
805 			XFTdebug("FcMatrixScale %g", ( double) requested_font. width / base_width);
806 		}
807 		if ( !IS_ZERO(requested_font. direction))
808 			FcMatrixRotate( &mat, cos(requested_font.direction * 3.14159265358 / 180.0), sin(requested_font.direction * 3.14159265358 / 180.0));
809 		FcPatternAddMatrix( request, FC_MATRIX, &mat);
810 	}
811 
812 	if ( guts. xft_no_antialias)
813 		FcPatternAddBool( request, FC_ANTIALIAS, 0);
814 
815 	/* match best font - must return something useful; the match is statically allocated */
816 	match = my_XftFontMatch( DISP, SCREEN, request, &res);
817 	if ( !match) {
818 		XFTdebug("XftFontMatch error");
819 		FcPatternDestroy( request);
820 		return false;
821 	}
822 	/* if (pguts->debug & DEBUG_FONTS) { FcPatternPrint(match); } */
823 	FcPatternDestroy( request);
824 
825 	/* xft does a rather bad job with synthesizing a monospaced
826 	font out of a proportional one ... try to find one ourself,
827 	or bail out if it is the case
828 	*/
829 	if ( requested_font.pitch == fpFixed && !force_xft_monospace_emulation) {
830 		int spacing = -1;
831 
832 		if (
833 			( FcPatternGetInteger( match, FC_SPACING, 0, &spacing) == FcResultMatch) &&
834 			( spacing != FC_MONO )
835 		) {
836 			Font font_with_family;
837 			char * monospace_font;
838 			font_with_family = requested_font;
839 			fcpattern2fontnames(match, &font_with_family);
840 			FcPatternDestroy( match);
841 
842 			if (!try_xft_monospace_emulation_by_name && ( monospace_font = find_good_font_by_family(&font_with_family, FC_MONO))) {
843 				/* try a good mono font, again */
844 				Bool ret;
845 				Font s = *source;
846 				strcpy(s.name, monospace_font);
847 				s.undef.name = 0;
848 				XFTdebug("try fixed pitch");
849 				try_xft_monospace_emulation_by_name++;
850 				ret = prima_xft_font_pick( self, &s, dest, size, xft_result);
851 				try_xft_monospace_emulation_by_name--;
852 				XFTdebug("fixed pitch done");
853 				return ret;
854 			} else {
855 				Bool ret;
856 				XFTdebug("force ugly monospace");
857 				force_xft_monospace_emulation++;
858 				ret = prima_xft_font_pick( self, source, dest, size, xft_result);
859 				force_xft_monospace_emulation--;
860 				return ret;
861 			}
862 		}
863 	} else if ( requested_font.pitch == fpVariable ) {
864 		/*
865 			xft picks a monospaced font when a proportional one is requested if the name points at it.
866 			Not that this is wrong, but in Prima terms pitch is heavier than name (this concept was borrowed from win32).
867 			So try to pick a variable font of the same family, if there is one. Same algorithm as with fixed fonts,
868 			but not as strict - if we can't find a proportional font within same family, so be it then
869 		*/
870 		int spacing = -1;
871 
872 		if (
873 			( FcPatternGetInteger( match, FC_SPACING, 0, &spacing) == FcResultMatch) &&
874 			( spacing == FC_MONO ) /* for our purpose all what is not FC_MONO is good enough to be fpVariable */
875 		) {
876 			Font font_with_family;
877 			char * proportional_font;
878 			font_with_family = requested_font;
879 			fcpattern2fontnames(match, &font_with_family);
880 
881 			if (( proportional_font = find_good_font_by_family(&font_with_family, FC_PROPORTIONAL))) {
882 				/* try a good variable font, again */
883 				Font s = *source;
884 				s.undef.name = 0;
885 				strcpy(s.name, proportional_font);
886 				XFTdebug("try variable pitch");
887 				FcPatternDestroy( match);
888 				return prima_xft_font_pick( self, &s, dest, size, xft_result);
889 			} else {
890 				XFTdebug("variable pitch is not found within family %s", font_with_family.family);
891 			}
892 		}
893 
894 	}
895 
896 	/* Manually check if font contains wanted encoding - matching by FcCharSet
897 		can't set threshold on how many glyphs can be omitted */
898 	if ( !(
899 		(strcmp( requested_font. encoding, fontspecific) != 0) ||
900 		(strcmp( requested_font. encoding, utf8_encoding) != 0)
901 	)) {
902 		FcCharSet *c = NULL;
903 		FcPatternGetCharSet( match, FC_CHARSET, 0, &c);
904 		if ( c && (FcCharSetCount(c) == 0)) {
905 			XFTdebug("charset mismatch (%s)", requested_font. encoding);
906 			FcPatternDestroy( match);
907 			return false;
908 		}
909 	}
910 
911 	/* Check if the matched font is scalable -- see comments in the beginning
912 		of the file about non-scalable fonts in Xft */
913 	if ( requested_font. vector > fvBitmap ) {
914 		FcBool scalable;
915 		if (( FcPatternGetBool( match, FC_SCALABLE, 0, &scalable) == FcResultMatch) && !scalable) {
916 			xft_build_font_key( &key, &requested_font, by_size);
917 			key. width = 0;
918 			hash_store( mismatch, &key, sizeof( FontKey), (void*)1);
919 			XFTdebug("refuse bitmapped font");
920 			FcPatternDestroy( match);
921 			return false;
922 		}
923 	}
924 
925 	/* XXX copy font details - very important these are correct !!! */
926 	/* strangely enough, if the match is used after XftFontOpenPattern, it is
927 		destroyed */
928 	loaded_font = requested_font;
929 	if ( !kf_base) {
930 		Bool underlined = loaded_font. style & fsUnderlined;
931 		Bool strike_out  = loaded_font. style & fsStruckOut;
932 		fcpattern2font( match, &loaded_font);
933 		if ( requested_font. width > 0) loaded_font. width = requested_font. width;
934 		if ( underlined) loaded_font. style |= fsUnderlined;
935 		if ( strike_out) loaded_font. style |= fsStruckOut;
936 	}
937 
938 
939 	/* check name match */
940 	{
941 		FcChar8 * s = NULL;
942 		FcPatternGetString( match, FC_FAMILY, 0, &s);
943 		if ( !s || strcmp(( const char*) s, requested_font. name) != 0) {
944 			int i, n = guts. n_fonts;
945 			PFontInfo info = guts. font_info;
946 
947 			if ( !guts. xft_priority) {
948 				XFTdebug("name mismatch");
949 			NAME_MISMATCH:
950 				xft_build_font_key( &key, &requested_font, by_size);
951 				key. width = 0;
952 				hash_store( mismatch, &key, sizeof( FontKey), (void*)1);
953 				FcPatternDestroy( match);
954 				return false;
955 			}
956 
957 			/* check if core has cached face name */
958 			if ( prima_find_known_font( &requested_font, false, by_size)) {
959 				XFTdebug("pass to cached core");
960 				goto NAME_MISMATCH;
961 			}
962 
963 			/* check if core has non-cached face name */
964 			for ( i = 0; i < n; i++) {
965 				if (
966 					info[i]. flags. disabled ||
967 					!info[i].flags.name ||
968 					(strcmp( info[i].font.name, requested_font.name) != 0)
969 				) continue;
970 				XFTdebug("pass to core");
971 				goto NAME_MISMATCH;
972 			}
973 		}
974 	}
975 
976 	/* load the font */
977 	xf = XftFontOpenPattern( DISP, match);
978 	if ( !xf) {
979 		xft_build_font_key( &key, &requested_font, by_size);
980 		key. width = 0;
981 		hash_store( mismatch, &key, sizeof( FontKey), (void*)1);
982 		XFTdebug("XftFontOpenPattern error");
983 		FcPatternDestroy( match);
984 		return false;
985 	}
986 	XFTdebug("load font %x", xf);
987 
988 	if ( kf_base) {
989 		/* A bit hacky, since the rotated font may be substituted by Xft.
990 		We skip the non-scalable fonts earlier to assure this doesn't happen,
991 		but anyway it's not 100% */
992 		Bool underlined = loaded_font. style & fsUnderlined;
993 		Bool strike_out  = loaded_font. style & fsStruckOut;
994 		loaded_font = kf_base-> font;
995 		loaded_font. direction = requested_font. direction;
996 		strcpy( loaded_font. encoding, csi-> name);
997 		if ( underlined) loaded_font. style |= fsUnderlined;
998 		if ( strike_out) loaded_font. style |= fsStruckOut;
999 	} else {
1000 		loaded_font. internalLeading = xf-> height - loaded_font. size * guts. resolution. y / 72.27 + 0.5;
1001 		if ( !by_size && !exact_pixel_size) {
1002 			/* Try to locate the corresponding size and
1003 			the correct height - FC_PIXEL_SIZE is not correct most probably
1004 			multiply size by 10 to address pixel-wise heights correctly.
1005 			*/
1006 			HeightGuessStack hgs;
1007 			int h, sz, last_sz = -1;
1008 			XftFont * guessed_font = NULL;
1009 
1010 			sz = 10.0 * (float) loaded_font. size * (float) loaded_font. height / (float) xf->height;
1011 			XFTdebug("need to figure the corresponding size - try %g first...", ( double) sz / 10.0);
1012 			guessed_font = try_size( self, loaded_font, ( double) sz / 10.0);
1013 
1014 			if ( guessed_font) {
1015 				h = guessed_font-> height;
1016 				XFTdebug("got height = %d", h);
1017 				if ( h != requested_font. height) {
1018 					XFTdebug("not good enough, try approximation from size %g", ( double) sz / 10.0);
1019 					prima_init_try_height( &hgs, requested_font. height, sz);
1020 					while ( 1) {
1021 						last_sz = sz;
1022 						sz = prima_try_height( &hgs, h);
1023 						if ( sz < 0) break;
1024 						guessed_font = try_size( self, loaded_font, ( double) sz / 10.0);
1025 						if ( !guessed_font) break;
1026 						h = guessed_font-> height;
1027 						XFTdebug("size %.1f got us %d pixels", ( float) sz / 10.0, h);
1028 						if ( h == 0 || h == requested_font. height) break;
1029 					}
1030 				}
1031 				if ( sz < 0) sz = last_sz;
1032 				if ( sz > 0) {
1033 					loaded_font. size = (double) sz / 10.0 + 0.5;
1034 					XFTdebug("found size: %.1f (%d)", ( float) sz / 10.0, loaded_font. size);
1035 				}
1036 			} else {
1037 				XFTdebug("found nothing");
1038 			}
1039 
1040 			if ( guessed_font && requested_font. height != xf-> height) {
1041 				xf = guessed_font;
1042 				loaded_font. height = xf-> height;
1043 				XFTdebug("redirect to font %x", xf);
1044 			}
1045 			XFTdebug("guessed size %d", loaded_font.size);
1046 		} else {
1047 			loaded_font. height = xf-> height;
1048 			XFTdebug("set height: %d", loaded_font.height);
1049 		}
1050 
1051 		loaded_font. maximalWidth = xf-> max_advance_width;
1052 		/* calculate average font width */
1053 		if ( loaded_font. pitch != fpFixed) {
1054 			FcChar32 c;
1055 			XftFont *x = kf_base ? kf_base-> xft : xf;
1056 			int num = 0, sum = 0;
1057 			for ( c = 63; c < 126; c+=4) {
1058 				FT_UInt ft_index;
1059 				XGlyphInfo glyph;
1060 				if ( !( ft_index = XftCharIndex( DISP, x, c))) continue;
1061 				XftGlyphExtents( DISP, x, &ft_index, 1, &glyph);
1062 				if ( glyph. xOff > 0 && glyph. xOff < xf->max_advance_width) {
1063 					sum += glyph. xOff;
1064 					num++;
1065 				} else
1066 					XFTdebug( "!! font %s returns bad XftGlyphExtents", loaded_font.name);
1067 			}
1068 			loaded_font. width = ( num > 10) ? ((float) sum / num + 0.5) : loaded_font. maximalWidth;
1069 		} else
1070 			loaded_font. width = loaded_font. maximalWidth;
1071 	}
1072 
1073 	{
1074 		XftFont * base = kf_base ? kf_base-> xft : xf;
1075 		loaded_font. descent = base-> descent;
1076 		loaded_font. ascent  = base-> ascent;
1077 	}
1078 
1079 	if ( cache_results ) {
1080 		/* create hash entry for subsequent loads of same font */
1081 		xft_store_font(&requested_font, &loaded_font, by_size, xf, kf_base ? kf_base-> xft : xf);
1082 		/* and with the matched by height and size */
1083 		for ( i = 0; i < 2; i++)
1084 			xft_store_font(&loaded_font, &loaded_font, i, xf, kf_base ? kf_base-> xft : xf);
1085 	}
1086 
1087 	*dest = loaded_font;
1088 	if ( xft_result ) *xft_result = xf;
1089 	return true;
1090 }
1091 
1092 Bool
prima_xft_set_font(Handle self,PFont font)1093 prima_xft_set_font( Handle self, PFont font)
1094 {
1095 	DEFXX;
1096 	CharSetInfo * csi;
1097 	PCachedFont kf = prima_xft_get_cache( font);
1098 	if ( !kf) return false;
1099 	XX-> font = kf;
1100 	if ( !( csi = hash_fetch( encodings, font-> encoding, strlen( font-> encoding))))
1101 		csi = locale;
1102 	XX-> xft_map8 = csi-> map;
1103 	if ( IS_ZERO(PDrawable( self)-> font. direction)) {
1104 		XX-> xft_font_sin = 0.0;
1105 		XX-> xft_font_cos = 1.0;
1106 	} else {
1107 		XX-> xft_font_sin = sin( font-> direction / 57.29577951);
1108 		XX-> xft_font_cos = cos( font-> direction / 57.29577951);
1109 	}
1110 	return true;
1111 }
1112 
1113 PCachedFont
prima_xft_get_cache(PFont font)1114 prima_xft_get_cache( PFont font)
1115 {
1116 	FontKey key;
1117 	PCachedFont kf;
1118 	xft_build_font_key( &key, font, false);
1119 	kf = ( PCachedFont) hash_fetch( guts. font_hash, &key, sizeof( FontKey));
1120 	if ( !kf || !kf-> xft) return NULL;
1121 	return kf;
1122 }
1123 
1124 /*
1125 	performs 3 types of queries:
1126 	defined(facename) - list of fonts with facename and encoding, if defined encoding
1127 	defined(encoding) - list of fonts with encoding
1128 	!defined(encoding) && !defined(facename) - list of all fonts with all available encodings.
1129 
1130 	since apc_fonts does the same and calls xft_fonts, array is the list of fonts
1131 	filled already, so xft_fonts reallocates the list when needed.
1132 
1133 	XXX - find proper font metrics, but where??
1134 */
1135 PFont
prima_xft_fonts(PFont array,const char * facename,const char * encoding,int * retCount)1136 prima_xft_fonts( PFont array, const char *facename, const char * encoding, int *retCount)
1137 {
1138 	FcFontSet * s;
1139 	FcPattern   *pat, **ppat;
1140 	FcObjectSet *os;
1141 	PFont newarray, f;
1142 	PHash names = NULL;
1143 	CharSetInfo * csi = NULL;
1144 	int i;
1145 
1146 	if ( encoding) {
1147 		if ( !( csi = ( CharSetInfo*) hash_fetch( encodings, encoding, strlen( encoding))))
1148 			return array;
1149 	}
1150 
1151 	pat = FcPatternCreate();
1152 	if ( facename) FcPatternAddString( pat, FC_FAMILY, ( FcChar8*) facename);
1153 	FcPatternAddBool( pat, FC_SCALABLE, 1);
1154 	os = FcObjectSetBuild( FC_FAMILY, FC_CHARSET, FC_ASPECT,
1155 		FC_SLANT, FC_WEIGHT, FC_SIZE, FC_PIXEL_SIZE, FC_SPACING,
1156 		FC_FOUNDRY, FC_SCALABLE, FC_DPI,
1157 		(void*) 0);
1158 	s = FcFontList( 0, pat, os);
1159 	FcObjectSetDestroy( os);
1160 	FcPatternDestroy( pat);
1161 	if ( !s) return array;
1162 
1163 	/* make dynamic */
1164 	if (( *retCount + s->nfont == 0) || !( newarray = realloc( array, sizeof(Font) * (*retCount + s-> nfont * ALL_CHARSETS)))) {
1165 		FcFontSetDestroy(s);
1166 		return array;
1167 	}
1168 	ppat = s-> fonts;
1169 	f = newarray + *retCount;
1170 	bzero( f, sizeof( Font) * s-> nfont * ALL_CHARSETS);
1171 
1172 	names = hash_create();
1173 
1174 	for ( i = 0; i < s->nfont; i++, ppat++) {
1175 		FcCharSet *c = NULL;
1176 		fcpattern2font( *ppat, f);
1177 		FcPatternGetCharSet( *ppat, FC_CHARSET, 0, &c);
1178 		if ( c && FcCharSetCount(c) == 0) continue;
1179 		if ( encoding) {
1180 			/* case 1 - encoding is set, filter only given encoding */
1181 
1182 			if ( c && ((signed) FcCharSetIntersectCount( csi-> fcs, c) >= csi-> glyphs - 1)) {
1183 				if ( !facename) {
1184 					/* and, if no facename set, each facename is reported only once */
1185 					if ( hash_fetch( names, f-> name, strlen( f-> name))) continue;
1186 					hash_store( names, f-> name, strlen( f-> name), ( void*)1);
1187 				}
1188 				strncpy( f-> encoding, encoding, 255);
1189 				f++;
1190 			}
1191 		} else if ( facename) {
1192 			/* case 2 - facename only is set, report each facename with every encoding */
1193 			int j;
1194 			Font * tmpl = f;
1195 			for ( j = 0; j < STD_CHARSETS; j++) {
1196 				if ( !std_charsets[j]. enabled) continue;
1197 				if ( FcCharSetIntersectCount( c, std_charsets[j]. fcs) >= std_charsets[j]. glyphs - 1) {
1198 					*f = *tmpl;
1199 					strncpy( f-> encoding, std_charsets[j]. name, 255);
1200 					f++;
1201 				}
1202 			}
1203 			/* if no encodings found, assume fontspecific, otherwise always provide iso10646 */
1204 			fcpattern2font( *ppat, f);
1205 			strcpy( f-> encoding, (f == tmpl) ? fontspecific : utf8_encoding);
1206 			f++;
1207 		} else if ( !facename && !encoding) {
1208 			/* case 3 - report unique facenames and store list of encodings
1209 				into the hack array */
1210 			if ( hash_fetch( names, f-> name, strlen( f-> name)) == (void*)1) continue;
1211 			hash_store( names, f-> name, strlen( f-> name), (void*)1);
1212 			if ( c) {
1213 				int j, found = 0;
1214 				char ** enc = (char**) f-> encoding;
1215 				unsigned char * shift = (unsigned char*)enc + sizeof(char *) - 1;
1216 				for ( j = 0; j < STD_CHARSETS; j++) {
1217 					if ( !std_charsets[j]. enabled) continue;
1218 					if ( *shift + 2 >= 256 / sizeof(char*)) break;
1219 					if ( FcCharSetIntersectCount( c, std_charsets[j]. fcs) >= std_charsets[j]. glyphs - 1) {
1220 						char buf[ 512];
1221 						int len = snprintf( buf, 511, "%s-charset-%s", f-> name, std_charsets[j]. name);
1222 						if ( hash_fetch( names, buf, len) == (void*)2) continue;
1223 						hash_store( names, buf, len, (void*)2);
1224 						*(enc + ++(*shift)) = std_charsets[j]. name;
1225 						found = 1;
1226 					}
1227 				}
1228 				*(enc + ++(*shift)) = found ? utf8_encoding : fontspecific;
1229 			}
1230 			f++;
1231 		}
1232 	}
1233 
1234 	*retCount = f - newarray;
1235 
1236 	hash_destroy( names, false);
1237 
1238 	FcFontSetDestroy(s);
1239 	return newarray;
1240 }
1241 
1242 void
prima_xft_font_encodings(PHash hash)1243 prima_xft_font_encodings( PHash hash)
1244 {
1245 	int i;
1246 	for ( i = 0; i < STD_CHARSETS; i++) {
1247 		if ( !std_charsets[i]. enabled) continue;
1248 		hash_store( hash, std_charsets[i]. name, strlen(std_charsets[i]. name), (void*) (std_charsets + i));
1249 	}
1250 	hash_store( hash, utf8_encoding, strlen(utf8_encoding), (void*) &utf8_charset);
1251 }
1252 
1253 static FcChar32 *
xft_text2ucs4(const unsigned char * text,int len,Bool utf8,uint32_t * map8)1254 xft_text2ucs4( const unsigned char * text, int len, Bool utf8, uint32_t * map8)
1255 {
1256 	FcChar32 *ret, *r;
1257 	if ( utf8) {
1258 		STRLEN charlen, bytelen = strlen(( const char *) text);
1259 		(void)bytelen;
1260 
1261 		if ( len < 0) len = prima_utf8_length(( char*) text, -1);
1262 		if ( !( r = ret = malloc( len * sizeof( FcChar32)))) return NULL;
1263 		while ( len--) {
1264 			*(r++) = prima_utf8_uvchr(text, bytelen, &charlen);
1265 			text += charlen;
1266 			bytelen -= charlen;
1267 			if ( charlen == 0 ) break;
1268 		}
1269 	} else {
1270 		int i;
1271 		if ( len < 0) len = strlen(( char*) text);
1272 		if ( !( ret = malloc( len * sizeof( FcChar32)))) return NULL;
1273 		for ( i = 0; i < len; i++)
1274 			ret[i] = ( text[i] < 128) ? text[i] : map8[ text[i] - 128];
1275 	}
1276 	return ret;
1277 }
1278 
1279 /*
1280 x11 has problems with text_out strings that are wider than
1281 64K pixel - it wraps the coordinates and produces mess. This hack is
1282 although ugly, but is better that X11 default behaviour, and
1283 at least can be excused by the fact that all GP spaces have
1284 their geometrical limits.
1285 */
1286 static int
check_width(PCachedFont self,int len)1287 check_width(PCachedFont self, int len)
1288 {
1289 	int div;
1290 	div = 65535L / (self-> font. maximalWidth ? self-> font. maximalWidth : 1);
1291 	if ( div <= 0) div = 1;
1292 	if ( len > div) len = div;
1293 	return len;
1294 }
1295 
1296 #define UPDATE_OVERHANGS(_len,_flags)                                             \
1297 	if ( i == 0) {                                                            \
1298 		if (( _flags & toAddOverhangs ) && glyph. x > 0) ret += glyph. x; \
1299 		if ( overhangs) overhangs-> x = (glyph.x > 0) ? glyph. x : 0;     \
1300 	}                                                                         \
1301 	if ( i == _len - 1) {                                                     \
1302 		int c = glyph. xOff - glyph. width + glyph. x;                    \
1303 		if ( (_flags & toAddOverhangs) && c < 0) ret -= c;                \
1304 		if ( overhangs) overhangs-> y = (c < 0) ? -c : 0;                 \
1305 	}
1306 
1307 int
prima_xft_get_text_width(PCachedFont self,const char * text,int len,int flags,uint32_t * map8,Point * overhangs)1308 prima_xft_get_text_width(
1309 	PCachedFont self, const char * text, int len, int flags,
1310 	uint32_t * map8, Point * overhangs
1311 ) {
1312 	int i, ret = 0, bytelen = 0;
1313 	XftFont * font = self-> xft_base;
1314 
1315 	if ( overhangs) overhangs-> x = overhangs-> y = 0;
1316 	if ( flags & toUTF8 ) bytelen = strlen(text);
1317 	len = check_width(self, len);
1318 
1319 	for ( i = 0; i < len; i++) {
1320 		FcChar32 c;
1321 		FT_UInt ft_index;
1322 		XGlyphInfo glyph;
1323 		if ( flags & toUTF8) {
1324 			STRLEN charlen;
1325 			c = ( FcChar32) prima_utf8_uvchr(text, bytelen, &charlen);
1326 			text += charlen;
1327 			bytelen -= charlen;
1328 			if ( charlen == 0 ) break;
1329 		} else if ( ((Byte*)text)[i] > 127) {
1330 			c = map8[ ((Byte*)text)[i] - 128];
1331 		} else
1332 			c = text[i];
1333 		ft_index = XftCharIndex( DISP, font, c);
1334 		XftGlyphExtents( DISP, font, &ft_index, 1, &glyph);
1335 		ret += glyph. xOff;
1336 		if ( (flags & toAddOverhangs ) || overhangs) {
1337 			UPDATE_OVERHANGS(len,flags)
1338 		}
1339 	}
1340 	return ret;
1341 }
1342 
1343 int
prima_xft_get_glyphs_width(PCachedFont self,PGlyphsOutRec t,Point * overhangs)1344 prima_xft_get_glyphs_width( PCachedFont self, PGlyphsOutRec t, Point * overhangs)
1345 {
1346 	int i, ret = 0;
1347 	FontContext fc;
1348 
1349 	font_context_init(&fc, &self->font, t->fonts, self->xft_base, NULL);
1350 	if ( overhangs) overhangs-> x = overhangs-> y = 0;
1351 
1352 	t->len = check_width(self, t->len);
1353 	for ( i = 0; i < t->len; i++) {
1354 		FT_UInt ft_index;
1355 		XGlyphInfo glyph;
1356 		ft_index = t->glyphs[i];
1357 		font_context_next(&fc);
1358 		XftGlyphExtents( DISP, fc.xft_font, &ft_index, 1, &glyph);
1359 		ret += glyph. xOff;
1360 		if ( (t->flags & toAddOverhangs ) || overhangs) {
1361 			UPDATE_OVERHANGS(t->len,t->flags)
1362 		}
1363 	}
1364 	return ret;
1365 }
1366 
1367 static Point *
get_box(Handle self,Point * ovx,int advance)1368 get_box( Handle self, Point * ovx, int advance )
1369 {
1370 	DEFXX;
1371 	Point * pt = ( Point *) malloc( sizeof( Point) * 5);
1372 	if ( !pt) return NULL;
1373 
1374 	if ( ovx->x < 0 ) ovx->x = 0;
1375 	if ( ovx->y < 0 ) ovx->y = 0;
1376 
1377 	pt[0].y = pt[2]. y = XX-> font-> font. ascent - 1;
1378 	pt[1].y = pt[3]. y = - XX-> font-> font. descent;
1379 	pt[4].y = 0;
1380 	pt[4].x = advance;
1381 	pt[3].x = pt[2]. x = advance + ovx->y;
1382 	pt[0].x = pt[1]. x = - ovx->x;
1383 
1384 	if ( !XX-> flags. paint_base_line) {
1385 		int i;
1386 		for ( i = 0; i < 4; i++) pt[i]. y += XX-> font-> font. descent;
1387 	}
1388 
1389 	if ( !IS_ZERO(PDrawable( self)-> font. direction)) {
1390 		int i;
1391 		double s = sin( PDrawable( self)-> font. direction / 57.29577951);
1392 		double c = cos( PDrawable( self)-> font. direction / 57.29577951);
1393 		for ( i = 0; i < 5; i++) {
1394 			double x = pt[i]. x * c - pt[i]. y * s;
1395 			double y = pt[i]. x * s + pt[i]. y * c;
1396 			pt[i]. x = x + (( x > 0) ? 0.5 : -0.5);
1397 			pt[i]. y = y + (( y > 0) ? 0.5 : -0.5);
1398 		}
1399 	}
1400 
1401 	return pt;
1402 }
1403 
1404 Point *
prima_xft_get_text_box(Handle self,const char * text,int len,int flags)1405 prima_xft_get_text_box( Handle self, const char * text, int len, int flags)
1406 {
1407 	Point ovx;
1408 	return get_box(self, &ovx, prima_xft_get_text_width(
1409 		X(self)-> font, text, len, flags,
1410 		X(self)-> xft_map8, &ovx)
1411 	);
1412 }
1413 
1414 Point *
prima_xft_get_glyphs_box(Handle self,PGlyphsOutRec t)1415 prima_xft_get_glyphs_box( Handle self, PGlyphsOutRec t)
1416 {
1417 	Point ovx;
1418 	return get_box(self, &ovx, prima_xft_get_glyphs_width(
1419 		X(self)-> font, t, &ovx
1420 	));
1421 }
1422 
1423 static XftFont *
create_no_aa_font(XftFont * font)1424 create_no_aa_font( XftFont * font)
1425 {
1426 	FcPattern * request;
1427 	if (!( request = FcPatternDuplicate( font-> pattern))) return NULL;
1428 	FcPatternDel( request, FC_ANTIALIAS);
1429 	FcPatternAddBool( request, FC_ANTIALIAS, 0);
1430 	return XftFontOpenPattern( DISP, request);
1431 }
1432 
1433 #define SORT(a,b)       { int swp; if ((a) > (b)) { swp=(a); (a)=(b); (b)=swp; }}
1434 #define REVERT(a)       (XX-> size. y - (a) - 1)
1435 #define SHIFT(a,b)      { (a) += XX-> gtransform. x + XX-> btransform. x; \
1436 									(b) += XX-> gtransform. y + XX-> btransform. y; }
1437 #define RANGE(a)        { if ((a) < -16383) (a) = -16383; else if ((a) > 16383) a = 16383; }
1438 #define RANGE2(a,b)     RANGE(a) RANGE(b)
1439 #define RANGE4(a,b,c,d) RANGE(a) RANGE(b) RANGE(c) RANGE(d)
1440 
1441 /* emulate win32 clearing off alpha bits on any Gdi operation */
1442 
1443 #define EMULATE_ALPHA_CLEARING 0
1444 
1445 static void
XftDrawGlyph_layered(PDrawableSysData selfxx,_Xconst XftColor * color,XftFont * font,int x,int y,_Xconst FT_UInt glyph)1446 XftDrawGlyph_layered( PDrawableSysData selfxx,
1447 	_Xconst XftColor *color, XftFont * font,
1448 	int x, int y, _Xconst FT_UInt glyph
1449 ) {
1450 	XftColor black;
1451 	XGlyphInfo extents;
1452 
1453 	XftGlyphExtents( DISP, font, &glyph, 1, &extents);
1454 
1455 	if (
1456 		!XX-> xft_shadow_pixmap ||
1457 		extents. width  > XX-> xft_shadow_extentions.x ||
1458 		extents. height > XX-> xft_shadow_extentions.y
1459 	) {
1460 		int w, h;
1461 		if ( XX-> xft_shadow_pixmap ) {
1462 			XFreePixmap( DISP, XX-> xft_shadow_pixmap);
1463 			XftDrawDestroy( XX-> xft_shadow_drawable);
1464 			XX-> xft_shadow_pixmap   = 0;
1465 			XX-> xft_shadow_drawable = NULL;
1466 		}
1467 		w = extents. width * 4;
1468 		h = extents. height * 4;
1469 		if ( w < 32 ) w = 32;
1470 		if ( h < 32 ) h = 32;
1471 		XX-> xft_shadow_extentions. x = w;
1472 		XX-> xft_shadow_extentions. y = h;
1473 		XX-> xft_shadow_pixmap   = XCreatePixmap( DISP, guts.root, w, h, 1);
1474 		XX-> xft_shadow_drawable = XftDrawCreateBitmap( DISP, XX-> xft_shadow_pixmap );
1475 	}
1476 
1477 	if ( !XX-> xft_shadow_gc ) {
1478 		XGCValues gcv;
1479 		gcv. foreground = 1;
1480 		XX-> xft_shadow_gc = XCreateGC( DISP, XX-> xft_shadow_pixmap, GCForeground, &gcv);
1481 	}
1482 
1483 	XFillRectangle( DISP, XX-> xft_shadow_pixmap, XX-> xft_shadow_gc,
1484 		0, 0, XX-> xft_shadow_extentions.x, XX-> xft_shadow_extentions.y);
1485 
1486 	black.color.red   =
1487 	black.color.green =
1488 	black.color.blue  =
1489 	black.pixel       = 0x1;
1490 	black.color.alpha = 0x0;
1491 	XftDrawGlyphs( XX-> xft_shadow_drawable, &black, font, extents.x, extents.y, &glyph, 1);
1492 	XftDrawGlyphs( XX-> xft_drawable, color, font, x, y, &glyph, 1);
1493 
1494 	XCopyPlane( DISP, XX-> xft_shadow_pixmap, XX-> gdrawable, XX-> gc, 0, 0, extents.width, extents.height,
1495 		x - extents.x, y - extents.y, 1);
1496 }
1497 
1498 /* When plotting rotated fonts, xft does not account for the accumulated
1499 	roundoff error, and thus the text line is shown at different angle
1500 	than requested. We track this and align the reference point when it
1501 	deviates from the ideal line */
1502 static void
xft_draw_glyphs(PDrawableSysData selfxx,_Xconst XftColor * color,int x,int y,_Xconst FcChar32 * string,int len,PGlyphsOutRec t)1503 xft_draw_glyphs( PDrawableSysData selfxx,
1504 		_Xconst XftColor *color, int x, int y,
1505 		_Xconst FcChar32 *string, int len,
1506 		PGlyphsOutRec t)
1507 {
1508 	XGCValues old_gcv, gcv;
1509 	int i, ox, oy, shift;
1510 	FontContext fc;
1511 	uint16_t * advances  = t ? t->advances : NULL;
1512 	int16_t  * positions = t ? t->positions : NULL;
1513 	Bool straight = IS_ZERO(XX-> font-> font. direction);
1514 
1515 	font_context_init(&fc, &XX->font->font,
1516 		t ? t->fonts : NULL,
1517 		XX->font->xft, advances ? NULL : XX->font->xft_base);
1518 
1519 	ox = x;
1520 	oy = y;
1521 	shift = 0;
1522 	if ( XX-> flags. layered && EMULATE_ALPHA_CLEARING) {
1523 		FT_UInt ft_index;
1524 		/* prepare xrender */
1525 		XftDrawGlyphs( XX-> xft_drawable, color, XX->font->xft, x, y, &ft_index, 0);
1526 
1527 		XGetGCValues( DISP, XX-> gc, GCFunction|GCBackground|GCForeground|GCPlaneMask, &old_gcv);
1528 		gcv. foreground = 0xffffffff;
1529 		gcv. background = 0x00000000;
1530 		gcv. function   = GXand;
1531 		gcv. plane_mask = guts. argb_bits. alpha_mask;
1532 		XChangeGC( DISP, XX-> gc, GCFunction|GCBackground|GCForeground|GCPlaneMask, &gcv);
1533 	}
1534 
1535 	if (t) len = t->len;
1536 	for ( i = 0; i < len; i++) {
1537 		int cx, cy, dx = 0, dy = 0;
1538 		FT_UInt ft_index;
1539 		XGlyphInfo glyph;
1540 
1541 		if ( t ) {
1542 			ft_index = t->glyphs[i];
1543 			font_context_next(&fc);
1544 			if ( advances ) {
1545 				register int x, y;
1546 				shift += *(advances++);
1547 				x = *(positions++);
1548 				y = *(positions++);
1549 				if ( straight ) {
1550 					dx = x;
1551 					dy = y;
1552 				} else {
1553 					dx = (int)(x * XX-> xft_font_cos + 0.5) - (int)(y * XX-> xft_font_sin + .5);
1554 					dy = (int)(x * XX-> xft_font_sin + 0.5) + (int)(y * XX-> xft_font_cos + .5);
1555 				}
1556 			} else
1557 				goto CHECK_EXTENTS;
1558 		} else {
1559 			ft_index = XftCharIndex( DISP, fc.xft_font, string[i]);
1560 		CHECK_EXTENTS:
1561 			XftGlyphExtents( DISP, fc.xft_base_font, &ft_index, 1, &glyph);
1562 			shift += glyph. xOff;
1563 		}
1564 		if ( straight ) {
1565 			cx = ox + shift;
1566 			cy = oy;
1567 		} else {
1568 			cx = ox + (int)(shift * XX-> xft_font_cos + 0.5);
1569 			cy = oy - (int)(shift * XX-> xft_font_sin + 0.5);
1570 		}
1571 		if ( XX-> flags. layered && EMULATE_ALPHA_CLEARING)
1572 			XftDrawGlyph_layered( XX, color, fc.xft_font, x + dx, y - dy, ft_index);
1573 		else
1574 			XftDrawGlyphs( XX-> xft_drawable, color, fc.xft_font, x + dx, y - dy, &ft_index, 1);
1575 		x = cx;
1576 		y = cy;
1577 	}
1578 
1579 	if ( XX-> flags. layered && EMULATE_ALPHA_CLEARING)
1580 		XChangeGC( DISP, XX-> gc, GCFunction|GCBackground|GCForeground|GCPlaneMask, &old_gcv);
1581 }
1582 
1583 static void
my_XftDrawString32(PDrawableSysData selfxx,_Xconst XftColor * color,int x,int y,_Xconst FcChar32 * string,int len)1584 my_XftDrawString32( PDrawableSysData selfxx,
1585 		_Xconst XftColor *color, int x, int y,
1586 		_Xconst FcChar32 *string, int len)
1587 {
1588 	if ( IS_ZERO(XX-> font-> font. direction) && !XX-> flags. layered )
1589 		XftDrawString32( XX-> xft_drawable, color, XX-> font-> xft, x, y, string, len);
1590 	else
1591 		xft_draw_glyphs( XX, color, x, y, string, len, NULL);
1592 }
1593 
1594 static int
filter_unsupported_rops(PDrawableSysData selfxx,int rop,XftColor * xftcolor)1595 filter_unsupported_rops( PDrawableSysData selfxx, int rop, XftColor * xftcolor )
1596 {
1597 	/* filter out unsupported rops */
1598 	switch ( rop) {
1599 	case ropBlackness:
1600 		xftcolor->color.red   =
1601 		xftcolor->color.green =
1602 		xftcolor->color.blue  =
1603 		xftcolor->pixel       = 0;
1604 		rop = ropCopyPut;
1605 		break;
1606 	case ropWhiteness:
1607 		xftcolor->color.red   =
1608 		xftcolor->color.green =
1609 		xftcolor->color.blue  = 0xffff;
1610 		xftcolor->pixel       = 0xffffffff;
1611 		rop = ropCopyPut;
1612 		break;
1613 	case ropXorPut:
1614 	case ropOrPut:
1615 	case ropNotSrcAnd:
1616 	case ropNotSrcXor:
1617 	case ropNotSrcOr:
1618 	case ropAndPut:
1619 		xftcolor->color.red   = COLOR_R16(XX->fore.color);
1620 		xftcolor->color.green = COLOR_G16(XX->fore.color);
1621 		xftcolor->color.blue  = COLOR_B16(XX->fore.color);
1622 		xftcolor->pixel       = XX-> fore. primary;
1623 		break;
1624 	case ropNotPut:
1625 		xftcolor->color.red   = COLOR_R16(~XX->fore.color);
1626 		xftcolor->color.green = COLOR_G16(~XX->fore.color);
1627 		xftcolor->color.blue  = COLOR_B16(~XX->fore.color);
1628 		xftcolor->pixel       = ~XX-> fore. primary;
1629 		rop = ropCopyPut;
1630 		break;
1631 	default:
1632 		xftcolor->color.red   = COLOR_R16(XX->fore.color);
1633 		xftcolor->color.green = COLOR_G16(XX->fore.color);
1634 		xftcolor->color.blue  = COLOR_B16(XX->fore.color);
1635 		xftcolor->pixel       = XX-> fore. primary;
1636 		rop = ropCopyPut;
1637 	}
1638 
1639 	return rop;
1640 }
1641 
1642 /* force-remove antialiasing, xft doesn't have a better API for this */
1643 static XftFont *
get_no_aa_font(PDrawableSysData selfxx,XftFont * font)1644 get_no_aa_font( PDrawableSysData selfxx, XftFont * font)
1645 {
1646 	FcBool aa;
1647 	if (
1648 		( FcPatternGetBool( font-> pattern, FC_ANTIALIAS, 0, &aa) == FcResultMatch)
1649 		&& aa
1650 	) {
1651 		XftFont * f = create_no_aa_font( font);
1652 		if ( f)
1653 			font = XX-> font-> xft_no_aa = f;
1654 	}
1655 
1656 	return font;
1657 }
1658 
1659 static void
setup_alpha(PDrawableSysData selfxx,XftColor * xftcolor,XftFont ** font)1660 setup_alpha(PDrawableSysData selfxx, XftColor * xftcolor, XftFont ** font)
1661 {
1662 	if ( XX-> flags. layered || !XX->type.bitmap) {
1663 		if ( selfxx->flags.antialias ) {
1664 			float div = 65535.0 / (float)(xftcolor->color.alpha = (selfxx->paint_alpha << 8));
1665 			xftcolor->color.red   = (float) xftcolor->color.red   / div;
1666 			xftcolor->color.green = (float) xftcolor->color.green / div;
1667 			xftcolor->color.blue  = (float) xftcolor->color.blue  / div;
1668 		} else
1669 			xftcolor->color.alpha = 0xffff;
1670 	} else {
1671 		xftcolor->color.alpha = (
1672 			(
1673 				xftcolor->color.red/3 +
1674 				xftcolor->color.green/3 +
1675 				xftcolor->color.blue/3
1676 			) > (0xff00 / 2)
1677 		) ?
1678 			0xffff :
1679 			0
1680 			;
1681 		if ( !guts. xft_no_antialias && !XX-> font-> xft_no_aa)
1682 			*font = get_no_aa_font(XX, *font);
1683 	}
1684 }
1685 
1686 static void
paint_text_background(Handle self,Point * p,int x,int y)1687 paint_text_background( Handle self, Point * p, int x, int y )
1688 {
1689 	DEFXX;
1690 	int i;
1691 	FillPattern fp;
1692 	memcpy( &fp, apc_gp_get_fill_pattern( self), sizeof( FillPattern));
1693 	XSetForeground( DISP, XX-> gc, XX-> back. primary);
1694 	XX-> flags. brush_back = 0;
1695 	XX-> flags. brush_fore = 1;
1696 	XX-> fore. balance = 0;
1697 	XSetFunction( DISP, XX-> gc, GXcopy);
1698 	apc_gp_set_fill_pattern( self, fillPatterns[fpSolid]);
1699 	for ( i = 0; i < 4; i++) {
1700 		p[i].x += x;
1701 		p[i].y += y;
1702 	}
1703 	i = p[2].x; p[2].x = p[3].x; p[3].x = i;
1704 	i = p[2].y; p[2].y = p[3].y; p[3].y = i;
1705 
1706 	apc_gp_fill_poly( self, 4, p);
1707 	apc_gp_set_rop( self, XX-> paint_rop);
1708 	apc_gp_set_color( self, XX-> fore. color);
1709 	apc_gp_set_fill_pattern( self, fp);
1710 }
1711 
1712 /* emulate over- and understriking */
1713 static void
overstrike(Handle self,int x,int y,Point * ovx,int advance)1714 overstrike( Handle self, int x, int y, Point *ovx, int advance)
1715 {
1716 	DEFXX;
1717 	float lw = apc_gp_get_line_width( self);
1718 	int d  = - PDrawable(self)-> font. descent;
1719 	int ay, x1, y1, x2, y2;
1720 	double c = XX-> xft_font_cos, s = XX-> xft_font_sin;
1721 
1722 	XSetFillStyle( DISP, XX-> gc, FillSolid);
1723 	if ( !XX-> flags. brush_fore) {
1724 		XSetForeground( DISP, XX-> gc, XX-> fore. primary);
1725 		XX-> flags. brush_fore = 1;
1726 	}
1727 
1728 	if ( lw != 1.0)
1729 		apc_gp_set_line_width( self, 1.0);
1730 
1731 	if ( ovx->x < 0 ) ovx->x = 0;
1732 	if ( ovx->y < 0 ) ovx->y = 0;
1733 	advance += ovx->y;
1734 
1735 	if ( PDrawable( self)-> font. style & fsUnderlined) {
1736 		ay = d;
1737 		x1 = x - ovx->x * c - ay * s + 0.5;
1738 		y1 = y - ovx->x * s + ay * c + 0.5;
1739 		x2 = x + advance * c - ay * s + 0.5;
1740 		y2 = y + advance * s + ay * c + 0.5;
1741 		XDrawLine( DISP, XX-> gdrawable, XX-> gc, x1, REVERT( y1), x2, REVERT( y2));
1742 	}
1743 
1744 	if ( PDrawable( self)-> font. style & fsStruckOut) {
1745 		ay = (XX-> font-> font.ascent - XX-> font-> font.internalLeading)/2;
1746 		x1 = x - ovx->x * c - ay * s + 0.5;
1747 		y1 = y - ovx->x * s + ay * c + 0.5;
1748 		x2 = x + advance * c - ay * s + 0.5;
1749 		y2 = y + advance * s + ay * c + 0.5;
1750 		XDrawLine( DISP, XX-> gdrawable, XX-> gc, x1, REVERT( y1), x2, REVERT( y2));
1751 	}
1752 
1753 	if ( lw != 1.0)
1754 		apc_gp_set_line_width( self, lw);
1755 }
1756 
1757 static void
allocate_xftdraw_surface(PDrawableSysData selfxx)1758 allocate_xftdraw_surface(PDrawableSysData selfxx)
1759 {
1760 	if ( !XX-> xft_drawable) {
1761 		if ( XX-> type. bitmap)
1762 			XX-> xft_drawable = XftDrawCreateBitmap( DISP, XX-> gdrawable );
1763 		else
1764 			XX-> xft_drawable = XftDrawCreate( DISP,
1765 				XX-> gdrawable, XX->visual->visual, XX->colormap
1766 			);
1767 		XftDrawSetSubwindowMode( XX-> xft_drawable,
1768 			XX-> flags.clip_by_children ? ClipByChildren : IncludeInferiors);
1769 		XCHECKPOINT;
1770 	}
1771 	if ( !XX-> flags. xft_clip) {
1772 		XftDrawSetClip( XX-> xft_drawable, XX-> current_region);
1773 		XX-> flags. xft_clip = 1;
1774 	}
1775 }
1776 
1777 typedef struct {
1778 	int x, y, dx, dy, width, height;
1779 	Pixmap canvas;
1780 	GC gc;
1781 } TextBlit;
1782 
1783 static Bool
open_text_blit(Handle self,int x,int y,int dx,int rop,TextBlit * tb)1784 open_text_blit(Handle self, int x, int y, int dx, int rop, TextBlit * tb)
1785 {
1786 	DEFXX;
1787 	XGCValues gcv;
1788 	int i;
1789 	Rect rc;
1790 	Point p[4], offset;
1791 
1792 	bzero( &rc, sizeof(rc));
1793 	tb-> x   = x;
1794 	tb-> y   = y;
1795 	tb-> dx  = dx;
1796 	tb-> dy  = XX-> font-> font. height;
1797 
1798 	offset. x = offset. y = 0;
1799 	p[0].x = p[2].x = 0;
1800 	p[0].y = p[1].y = 0;
1801 	p[1].x = p[3].x = tb->dx;
1802 	p[2].y = p[3].y = tb->dy;
1803 	rc. left = rc. right = rc. top = rc. bottom = 0;
1804 	for ( i = 1; i < 4; i++) {
1805 		int x = p[i].x * XX-> xft_font_cos - p[i].y * XX-> xft_font_sin + 0.5;
1806 		int y = p[i].x * XX-> xft_font_sin + p[i].y * XX-> xft_font_cos + 0.5;
1807 		if ( rc. left    > x) rc. left   = x;
1808 		if ( rc. right   < x) rc. right  = x;
1809 		if ( rc. bottom  > y) rc. bottom = y;
1810 		if ( rc. top     < y) rc. top    = y;
1811 	}
1812 	tb->width  = rc. right  - rc. left   + 1;
1813 	tb->height = rc. top    - rc. bottom + 1;
1814 
1815 	tb->canvas = XCreatePixmap( DISP, guts. root,
1816 		tb->width, tb->height,
1817 		XX-> type. bitmap ? 1 : guts. depth);
1818 	if ( !tb->canvas)
1819 		return false;
1820 
1821 	tb->dx = -rc. left;
1822 	tb->dy = -rc. bottom;
1823 	tb->gc = XCreateGC( DISP, tb->canvas, 0, &gcv);
1824 	switch ( rop) {
1825 	case ropAndPut:
1826 	case ropNotSrcXor:
1827 	case ropNotSrcOr:
1828 		XSetForeground( DISP, tb->gc, 0xffffffff);
1829 		break;
1830 	default:
1831 		XSetForeground( DISP, tb->gc, 0x0);
1832 		break;
1833 	}
1834 	XFillRectangle( DISP, tb->canvas, tb->gc, 0, 0, tb->width, tb->height);
1835 	XftDrawChange( XX-> xft_drawable, tb->canvas);
1836 	if ( XX-> flags. xft_clip)
1837 		XftDrawSetClip( XX-> xft_drawable, 0);
1838 
1839 	return true;
1840 }
1841 
1842 static void
close_text_blit(PDrawableSysData selfxx,TextBlit * tb)1843 close_text_blit(PDrawableSysData selfxx, TextBlit * tb)
1844 {
1845 	XftDrawChange( XX-> xft_drawable, XX-> gdrawable);
1846 	if ( XX-> flags. xft_clip)
1847 		XftDrawSetClip( XX-> xft_drawable, XX-> current_region);
1848 	XCHECKPOINT;
1849 	XCopyArea( DISP,
1850 		tb->canvas, XX-> gdrawable, XX-> gc,
1851 		0, 0, tb->width, tb->height,
1852 		tb->x - tb->dx, REVERT( tb->y - tb->dy + tb->height)
1853 	);
1854 	XFreeGC( DISP, tb->gc);
1855 	XFreePixmap( DISP, tb->canvas);
1856 }
1857 
1858 /*
1859 	If you're guided here by something like
1860 
1861 		X Error: BadLength (poly request too large or internal Xlib length error),
1862 
1863 	then you probably need to know that your X server is out of date.
1864 	The error is caused by a Xrender bug, and you have the following options:
1865 	- update your X server
1866 	- set guts.xft_disable_large_fonts to 1, which would explicitly tell Prima not to wait
1867 	for Xlib to bark on large polygons
1868 	- set guts.xft_disable_large_fonts to 1 and decrease MAX_GLYPH_SIZE, if the former
1869 	option is not enough
1870 */
1871 
1872 Bool
prima_xft_text_out(Handle self,const char * text,int x,int y,int len,int flags)1873 prima_xft_text_out( Handle self, const char * text, int x, int y, int len, int flags)
1874 {
1875 	DEFXX;
1876 	FcChar32 *ucs4;
1877 	XftColor xftcolor;
1878 	XftFont *font = XX-> font-> xft;
1879 	int rop = XX-> paint_rop;
1880 	Point baseline;
1881 
1882 	if ( len == 0) return true;
1883 	len = check_width( XX->font, len );
1884 	rop = filter_unsupported_rops( XX, rop, &xftcolor );
1885 	setup_alpha( XX, &xftcolor, &font );
1886 
1887 	/* paint background if opaque */
1888 	if ( XX-> flags. paint_opaque) {
1889 		Point * p = prima_xft_get_text_box( self, text, len, flags);
1890 		paint_text_background( self, p, x, y );
1891 		free( p);
1892 	}
1893 
1894 	SHIFT(x,y);
1895 	RANGE2(x,y);
1896 	/* xft doesn't allow shifting glyph reference point - need to adjust manually */
1897 	baseline.x = - PDrawable(self)-> font. descent * XX-> xft_font_sin;
1898 	baseline.y = - PDrawable(self)-> font. descent * ( 1 - XX-> xft_font_cos )
1899 					+ XX-> font-> font. descent;
1900 	if ( !XX-> flags. paint_base_line) {
1901 		x += baseline.x;
1902 		y += baseline.y;
1903 	}
1904 
1905 	/* convert text string to unicode */
1906 	if ( !( ucs4 = xft_text2ucs4(( unsigned char*) text, len, flags & toUTF8, X( self)-> xft_map8)))
1907 		return false;
1908 
1909 	allocate_xftdraw_surface(XX);
1910 	if ( rop != ropCopyPut) {
1911 		/* emulate rops by blitting the text */
1912 		int dx;
1913 		TextBlit tb;
1914 		dx = prima_xft_get_text_width( XX-> font, text, len,
1915 			flags | toAddOverhangs, X(self)-> xft_map8, NULL);
1916 		if (!open_text_blit(self, x, y, dx, rop, &tb))
1917 			goto COPY_PUT;
1918 		my_XftDrawString32( XX, &xftcolor,
1919 			tb.dx + baseline.x, tb.height - tb.dy - baseline.y,
1920 			ucs4, len);
1921 		close_text_blit(XX, &tb);
1922 		x -= baseline.x;
1923 		y -= baseline.y;
1924 	} else {
1925 	COPY_PUT:
1926 		my_XftDrawString32( XX, &xftcolor, x, REVERT( y) + 1, ucs4, len);
1927 	}
1928 	free( ucs4);
1929 	XCHECKPOINT;
1930 
1931 	if ( PDrawable( self)-> font. style & (fsUnderlined|fsStruckOut)) {
1932 		Point ovx;
1933 		overstrike(self, x, y, &ovx, prima_xft_get_text_width(
1934 			XX-> font, text, len, flags | toAddOverhangs,
1935 			X(self)-> xft_map8, &ovx) - 1
1936 		);
1937 	}
1938 	XFLUSH;
1939 
1940 	return true;
1941 }
1942 
1943 Bool
prima_xft_glyphs_out(Handle self,PGlyphsOutRec t,int x,int y)1944 prima_xft_glyphs_out( Handle self, PGlyphsOutRec t, int x, int y)
1945 {
1946 	DEFXX;
1947 	XftColor xftcolor;
1948 	XftFont *font = XX-> font-> xft;
1949 	int rop = XX-> paint_rop;
1950 	Point baseline;
1951 
1952 	t-> flags |= toAddOverhangs; /* for overstriking etc */
1953 
1954 	if ( t->len == 0) return true;
1955 	t->len = check_width( XX->font, t->len );
1956 	rop = filter_unsupported_rops( XX, rop, &xftcolor );
1957 	setup_alpha( XX, &xftcolor, &font );
1958 
1959 	/* paint background if opaque */
1960 	if ( XX-> flags. paint_opaque) {
1961 		Point * p = prima_xft_get_glyphs_box( self, t);
1962 		paint_text_background( self, p, x, y );
1963 		free( p);
1964 	}
1965 
1966 	SHIFT(x,y);
1967 	RANGE2(x,y);
1968 	/* xft doesn't allow shifting glyph reference point - need to adjust manually */
1969 	baseline.x = - PDrawable(self)-> font. descent * XX-> xft_font_sin;
1970 	baseline.y = - PDrawable(self)-> font. descent * ( 1 - XX-> xft_font_cos )
1971 					+ XX-> font-> font. descent;
1972 	if ( !XX-> flags. paint_base_line) {
1973 		x += baseline.x;
1974 		y += baseline.y;
1975 	}
1976 
1977 	allocate_xftdraw_surface(XX);
1978 	if ( rop != ropCopyPut) {
1979 		/* emulate rops by blitting the text */
1980 		int dx;
1981 		TextBlit tb;
1982 		dx = prima_xft_get_glyphs_width( XX-> font, t, NULL);
1983 		if (!open_text_blit(self, x, y, dx, rop, &tb))
1984 			goto COPY_PUT;
1985 		xft_draw_glyphs(XX, &xftcolor,
1986 			tb.dx + baseline.x, tb.height - tb.dy - baseline.y,
1987 			NULL, 0, t);
1988 		close_text_blit(XX, &tb);
1989 		x -= baseline.x;
1990 		y -= baseline.y;
1991 	} else {
1992 	COPY_PUT:
1993 		xft_draw_glyphs(XX, &xftcolor, x, REVERT(y) + 1, NULL, 0, t);
1994 	}
1995 	XCHECKPOINT;
1996 
1997 	if ( PDrawable( self)-> font. style & (fsUnderlined|fsStruckOut)) {
1998 		Point ovx;
1999 		t-> flags |= toAddOverhangs;
2000 		overstrike(self, x, y, &ovx, prima_xft_get_glyphs_width(
2001 			XX-> font, t, &ovx) - 1);
2002 	}
2003 	XFLUSH;
2004 
2005 	return true;
2006 }
2007 
2008 static Bool
xft_add_item(unsigned long ** list,int * count,int * size,FcChar32 chr,Bool decrease_count_if_failed)2009 xft_add_item( unsigned long ** list, int * count, int * size, FcChar32 chr,
2010 				Bool decrease_count_if_failed)
2011 {
2012 	if ( *count >= *size) {
2013 		unsigned long * newlist = realloc( *list, sizeof( unsigned long) * ( *size *= 2));
2014 		if ( !newlist) {
2015 			if ( decrease_count_if_failed) (*count)--;
2016 			return false;
2017 		}
2018 		*list = newlist;
2019 	}
2020 	(*list)[(*count)++] = ( unsigned long ) chr;
2021 	return true;
2022 }
2023 
2024 static unsigned long *
get_font_ranges(FcCharSet * c,int * count)2025 get_font_ranges( FcCharSet *c, int * count)
2026 {
2027 	FcChar32 ucs4, last = 0, haslast = 0;
2028 	FcChar32 map[FC_CHARSET_MAP_SIZE];
2029 	FcChar32 next;
2030 	int size = 16;
2031 	unsigned long * ret;
2032 
2033 #define ADD(ch,flag) if(!xft_add_item(&ret,count,&size,ch,flag)) return ret
2034 
2035 	*count = 0;
2036 	if ( !c) return false;
2037 	if ( !( ret = malloc( sizeof( unsigned long) * size))) return NULL;
2038 
2039 	if ( FcCharSetCount(c) == 0) {
2040 		/* better than nothing */
2041 		ADD( 32, true);
2042 		ADD( 128, false);
2043 		return ret;
2044 	}
2045 
2046 	for (ucs4 = FcCharSetFirstPage (c, map, &next);
2047 		ucs4 != FC_CHARSET_DONE;
2048 		ucs4 = FcCharSetNextPage (c, map, &next))
2049 	{
2050 		int i, j;
2051 		for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
2052 			if (map[i]) {
2053 				for (j = 0; j < 32; j++)
2054 					if (map[i] & (1 << j)) {
2055 						FcChar32 u = ucs4 + i * 32 + j;
2056 						if ( haslast) {
2057 							if ( last != u - 1) {
2058 								ADD( last,true);
2059 								ADD( u,false);
2060 							}
2061 						} else {
2062 							ADD( u,false);
2063 							haslast = 1;
2064 						}
2065 						last = u;
2066 					}
2067 			}
2068 	}
2069 	if ( haslast) ADD( last,true);
2070 
2071 	return ret;
2072 }
2073 
2074 unsigned long *
prima_xft_get_font_ranges(Handle self,int * count)2075 prima_xft_get_font_ranges( Handle self, int * count)
2076 {
2077 	return get_font_ranges(X(self)-> font-> xft-> charset, count);
2078 }
2079 
2080 char *
prima_xft_get_font_languages(Handle self)2081 prima_xft_get_font_languages( Handle self)
2082 {
2083 #if FC_VERSION >= 21100
2084 	FcPattern *pat;
2085 	FcLangSet *ls;
2086 	FcStrSet  *ss;
2087 	FcStrList *l;
2088 	FcChar8  *s;
2089 	char *ret, *p;
2090 	int size;
2091 
2092 	if ( !(pat = X(self)-> font-> xft-> pattern))
2093 		return NULL;
2094 	FcPatternGetLangSet(pat, FC_LANG, 0, &ls);
2095 	if ( !ls )
2096 		return NULL;
2097 	if ( !(ss = FcLangSetGetLangs(ls)))
2098 		return NULL;
2099 	if ( !(l = FcStrListCreate (ss)))
2100 		return NULL;
2101 
2102 	size = 1024; /* longest line from 'fc-list -v' is 779 */
2103 	if ( !(p = ret = malloc(size)))
2104 		goto FAIL;
2105 
2106 	FcStrListFirst(l);
2107 	while ((s = FcStrListNext(l)) != NULL) {
2108 		int len = strlen((char*)s);
2109 		if ( p - ret + len + 1 + 1 > size ) {
2110 			char * p2;
2111 			size *= 2;
2112 			if ( !( p2 = realloc(ret, size)))
2113 				goto FAIL;
2114 			p   = p2 + (p - ret);
2115 			ret = p2;
2116 		}
2117 		strcpy( p, (char*) s );
2118 		p += len + 1;
2119 	}
2120 	*p = 0;
2121 	FcStrListDone(l);
2122 	return ret;
2123 
2124 FAIL:
2125 	FcStrListDone(l);
2126 	free(ret);
2127 #endif
2128 	return NULL;
2129 }
2130 
2131 PFontABC
prima_xft_get_font_abc(Handle self,int firstChar,int lastChar,int flags)2132 prima_xft_get_font_abc( Handle self, int firstChar, int lastChar, int flags)
2133 {
2134 	PFontABC abc;
2135 	int i, len = lastChar - firstChar + 1;
2136 	XftFont *font = X(self)-> font-> xft_base;
2137 
2138 	if ( !( abc = malloc( sizeof( FontABC) * len)))
2139 		return NULL;
2140 
2141 	for ( i = 0; i < len; i++) {
2142 		FT_UInt ft_index;
2143 		XGlyphInfo glyph;
2144 		if ( flags & toGlyphs ) {
2145 			ft_index = i + firstChar;
2146 		} else {
2147 			FcChar32 c = i + firstChar;
2148 			if ( !(flags & toUnicode) && c > 128)
2149 				c = X(self)-> xft_map8[ c - 128];
2150 			ft_index = XftCharIndex( DISP, font, c);
2151 		}
2152 		XftGlyphExtents( DISP, font, &ft_index, 1, &glyph);
2153 		abc[i]. a = -glyph. x;
2154 		abc[i]. b = glyph. width;
2155 		abc[i]. c = glyph. xOff - glyph. width + glyph. x;
2156 	}
2157 
2158 	return abc;
2159 }
2160 
2161 PFontABC
prima_xft_get_font_def(Handle self,int firstChar,int lastChar,int flags)2162 prima_xft_get_font_def( Handle self, int firstChar, int lastChar, int flags)
2163 {
2164 	PFontABC abc;
2165 	int i, len = lastChar - firstChar + 1;
2166 	XftFont *font = X(self)-> font-> xft_base;
2167 
2168 	if ( !( abc = malloc( sizeof( FontABC) * len)))
2169 		return NULL;
2170 
2171 	for ( i = 0; i < len; i++) {
2172 		FT_UInt ft_index;
2173 		XGlyphInfo glyph;
2174 		if ( flags & toGlyphs ) {
2175 			ft_index = i + firstChar;
2176 		} else {
2177 			FcChar32 c = i + firstChar;
2178 			if ( !(flags & toUnicode) && c > 128)
2179 				c = X(self)-> xft_map8[ c - 128];
2180 			ft_index = XftCharIndex( DISP, font, c);
2181 		}
2182 		XftGlyphExtents( DISP, font, &ft_index, 1, &glyph);
2183 		abc[i]. a = X(self)-> font-> font. descent - glyph. height + glyph. y; /* XXX yOff ? */
2184 		abc[i]. b = glyph. height;
2185 		abc[i]. c = X(self)-> font-> font. ascent - glyph. y;
2186 	}
2187 
2188 	return abc;
2189 }
2190 
2191 uint32_t *
prima_xft_map8(const char * encoding)2192 prima_xft_map8( const char * encoding)
2193 {
2194 	CharSetInfo * csi;
2195 	if ( !( csi = hash_fetch( encodings, encoding, strlen( encoding))))
2196 		csi = locale;
2197 	return csi-> map;
2198 }
2199 
2200 Bool
prima_xft_parse(char * ppFontNameSize,Font * font)2201 prima_xft_parse( char * ppFontNameSize, Font * font)
2202 {
2203 	FcPattern * p = FcNameParse(( FcChar8*) ppFontNameSize);
2204 	FcCharSet * c = NULL;
2205 	Font f, def = guts. default_font;
2206 
2207 	bzero( &f, sizeof( Font));
2208 	f. undef. height = f. undef. width = f. undef. size = f. undef. vector = f. undef. pitch = 1;
2209 	fcpattern2font( p, &f);
2210 	f. undef. width = 1;
2211 	FcPatternGetCharSet( p, FC_CHARSET, 0, &c);
2212 	if ( c && (FcCharSetCount(c) > 0)) {
2213 		int i;
2214 		for ( i = 0; i < STD_CHARSETS; i++) {
2215 			if ( !std_charsets[i]. enabled) continue;
2216 			if ( FcCharSetIntersectCount( std_charsets[i]. fcs, c) >= std_charsets[i]. glyphs - 1) {
2217 				strcpy( f. encoding, std_charsets[i]. name);
2218 				break;
2219 			}
2220 		}
2221 	}
2222 	FcPatternDestroy( p);
2223 	if ( !prima_xft_font_pick( NULL_HANDLE, &f, &def, NULL, NULL)) return false;
2224 	*font = def;
2225 	XFTdebug( "parsed ok: %d.%s", def.size, def.name);
2226 	return true;
2227 }
2228 
2229 void
prima_xft_update_region(Handle self)2230 prima_xft_update_region( Handle self)
2231 {
2232 	DEFXX;
2233 	if ( XX-> xft_drawable) {
2234 		XftDrawSetClip( XX-> xft_drawable, XX-> current_region);
2235 		XX-> flags. xft_clip = 1;
2236 	}
2237 }
2238 
2239 int
prima_xft_load_font(char * filename)2240 prima_xft_load_font( char* filename)
2241 {
2242 	int count = 0;
2243 	struct stat s;
2244 	FcPattern * font;
2245 	FcConfig * config;
2246 	FcFontSet *fonts;
2247 
2248 	/* -f $filename or die */
2249 	if (stat( filename, &s) < 0) {
2250 		warn("%s", strerror(errno));
2251 		return 0;
2252 	}
2253 
2254 #define FAIL(msg) { warn(msg); return 0; }
2255 
2256 	if (( s.st_mode & S_IFDIR) != 0)
2257 		FAIL("Must not be a directory")
2258 	if ( !( font = FcFreeTypeQuery ((const FcChar8*)filename, 0, NULL, &count)))
2259 		FAIL("Format not recognized")
2260 	FcPatternDestroy (font);
2261 
2262 	if ( count == 0 )
2263 		FAIL("No fonts found in file")
2264 	if ( !(config = FcConfigGetCurrent()))
2265 		FAIL("FcConfigGetCurrent error")
2266 	if ( !( fonts = FcConfigGetFonts(config, FcSetSystem)))
2267 		FAIL("FcConfigGetFonts(FcSetSystem) error")
2268 	if ( !( FcFileScan(fonts, NULL, NULL, NULL, (const FcChar8*)filename, FcFalse)))
2269 		FAIL("FcFileScan error")
2270 
2271 	return count;
2272 }
2273 
2274 void
prima_xft_gp_destroy(Handle self)2275 prima_xft_gp_destroy( Handle self )
2276 {
2277 	DEFXX;
2278 	if ( XX-> xft_drawable) {
2279 		XftDrawDestroy( XX-> xft_drawable);
2280 		XX-> xft_drawable = NULL;
2281 	}
2282 	if ( XX-> xft_shadow_drawable) {
2283 		XftDrawDestroy( XX-> xft_shadow_drawable);
2284 		XX-> xft_shadow_drawable = NULL;
2285 	}
2286 	if ( XX-> xft_shadow_pixmap) {
2287 		XFreePixmap( DISP, XX-> xft_shadow_pixmap);
2288 		XX-> xft_shadow_pixmap = 0;
2289 	}
2290 	if ( XX-> xft_shadow_gc) {
2291 		XFreeGC( DISP, XX-> xft_shadow_gc);
2292 		XX-> xft_shadow_gc = 0;
2293 	}
2294 }
2295 
2296 typedef struct {
2297 	int count, size, last_ptr;
2298 	int *buffer;
2299 } OutlineStorage;
2300 
2301 #define STORE_POINT(p) if(p) {\
2302 	storage->buffer[ storage->last_ptr + 1 ]++;\
2303 	storage->buffer[ storage->count++ ] = p->x;\
2304 	storage->buffer[ storage->count++ ] = p->y;\
2305 }
2306 
2307 static int
store_command(OutlineStorage * storage,int cmd,const FT_Vector * p1,const FT_Vector * p2,const FT_Vector * p3)2308 store_command( OutlineStorage * storage, int cmd, const FT_Vector * p1, const FT_Vector * p2, const FT_Vector * p3)
2309 {
2310 	if ( storage-> size == 0 ) {
2311 		storage-> size = 256;
2312 		if ( !( storage-> buffer = malloc(sizeof(int) * storage->size)))
2313 			return 1;
2314 
2315 	} else if ( storage-> count + 7 >= storage->size ) {
2316 		int * r;
2317 		storage-> size *= 2;
2318 		if (( r = realloc( storage->buffer, sizeof(int) * storage->size)) == NULL ) {
2319 			warn("Not enough memory");
2320 			free( storage-> buffer );
2321 			storage-> buffer = NULL;
2322 			storage-> count = 0;
2323 			return 1;
2324 		}
2325 		storage-> buffer = r;
2326 	}
2327 
2328 	if (
2329 		storage-> last_ptr < 0 || storage->buffer[storage->last_ptr] != cmd ||
2330 		cmd != ggoLine
2331 	) {
2332 		storage->last_ptr = storage->count;
2333 		storage->buffer[ storage->count++ ] = cmd;
2334 		storage->buffer[ storage->count++ ] = 0;
2335 	}
2336 
2337 	STORE_POINT(p1)
2338 	STORE_POINT(p2)
2339 	STORE_POINT(p3)
2340 
2341 	return 0;
2342 }
2343 
2344 static int
ftoutline_line(const FT_Vector * to,void * user)2345 ftoutline_line(const FT_Vector* to, void* user)
2346 {
2347 	return store_command((OutlineStorage*)user, ggoLine, to, NULL, NULL);
2348 }
2349 
2350 static int
ftoutline_move(const FT_Vector * to,void * user)2351 ftoutline_move(const FT_Vector* to, void* user)
2352 {
2353 	return store_command((OutlineStorage*)user, ggoMove, to, NULL, NULL);
2354 }
2355 
2356 static int
ftoutline_conic(const FT_Vector * control,const FT_Vector * to,void * user)2357 ftoutline_conic(const FT_Vector* control, const FT_Vector* to, void* user)
2358 {
2359 	return store_command((OutlineStorage*)user, ggoConic, control, to, NULL);
2360 }
2361 
2362 static int
ftoutline_cubic(const FT_Vector * control1,const FT_Vector * control2,const FT_Vector * to,void * user)2363 ftoutline_cubic(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user)
2364 {
2365 	return store_command((OutlineStorage*)user, ggoCubic, control1, control2, to);
2366 }
2367 
2368 int
prima_xft_get_glyph_outline(Handle self,int index,int flags,int ** buffer)2369 prima_xft_get_glyph_outline( Handle self, int index, int flags, int ** buffer)
2370 {
2371 	DEFXX;
2372 	FcChar32 c;
2373 	FT_Face face;
2374 	FT_UInt ft_index;
2375 	FT_Int32 ft_flags = FT_LOAD_NO_BITMAP |
2376 		(( flags & ggoUseHints ) ? 0 : FT_LOAD_NO_HINTING);
2377 	FT_Outline_Funcs funcs = {
2378 		ftoutline_move,
2379 		ftoutline_line,
2380 		ftoutline_conic,
2381 		ftoutline_cubic,
2382 		0, 0
2383 	};
2384 	OutlineStorage storage = { 0, 0, -1, NULL };
2385 
2386 	if ( !( face = XftLockFace( XX->font->xft)))
2387 		return -1;
2388 
2389 	c = ((flags & (ggoUnicode|ggoGlyphIndex)) || index <= 128) ? index : XX-> xft_map8[index - 128];
2390 	ft_index = (flags & ggoGlyphIndex) ? c : XftCharIndex( DISP, XX->font->xft, c);
2391 	if (
2392 		(FT_Load_Glyph (face, ft_index, ft_flags) == 0) &&
2393 		(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
2394 	)
2395   		FT_Outline_Decompose( &face->glyph->outline, &funcs, (void*)&storage);
2396 
2397 	XftUnlockFace(XX->font->xft);
2398 
2399 	*buffer = storage.buffer;
2400 	return storage.count;
2401 }
2402 
2403 unsigned long *
prima_xft_mapper_query_ranges(PFont font,int * count,unsigned int * flags)2404 prima_xft_mapper_query_ranges(PFont font, int * count, unsigned int * flags)
2405 {
2406 	char name[256];
2407 	XftFont * xft = NULL;
2408 	unsigned long * ranges;
2409 	strncpy(name, font->name, 256);
2410 	prima_xft_font_pick( NULL_HANDLE, font, font, NULL, &xft);
2411 	*flags = 0 | MAPPER_FLAGS_SYNTHETIC_PITCH;
2412 	if ( !xft || strcmp( font->name, name ) != 0 ) {
2413 		*count = 0;
2414 		return NULL;
2415 	}
2416 	ranges = get_font_ranges( xft->charset, count);
2417 #ifdef WITH_HARFBUZZ
2418 {
2419 	FT_Face face;
2420 	hb_buffer_t *buf;
2421 	hb_font_t *font;
2422 	hb_glyph_position_t *glyph_pos;
2423 	unsigned int l = 0;
2424 	uint32_t x_tilde[2] = {'x', 0x330};
2425 
2426 	if ( !( face = XftLockFace( xft)))
2427 		return ranges;
2428 
2429 	buf = hb_buffer_create();
2430 	hb_buffer_add_utf32(buf, x_tilde, 2, 0, -1);
2431 	hb_buffer_guess_segment_properties (buf);
2432 	font = hb_ft_font_create(face, NULL);
2433 	hb_shape(font, buf, NULL, 0);
2434 	glyph_pos  = hb_buffer_get_glyph_positions(buf, &l);
2435 
2436 	if ( l == 2 && glyph_pos[1].x_advance == 0 ) {
2437 		*flags |= MAPPER_FLAGS_COMBINING_SUPPORTED;
2438 	}
2439 
2440 	hb_buffer_destroy(buf);
2441 	hb_font_destroy(font);
2442 	XftUnlockFace(xft);
2443 
2444 }
2445 #endif
2446 	return ranges;
2447 }
2448 
2449 Bool
prima_xft_text_shaper(Handle self,PTextShapeRec r,uint32_t * map8)2450 prima_xft_text_shaper( Handle self, PTextShapeRec r, uint32_t * map8)
2451 {
2452         int i;
2453 	uint16_t *glyphs;
2454 	uint32_t *text;
2455 	XftFont *font = X(self)->font->xft_base;
2456 
2457         for (
2458 		i = 0, glyphs = r->glyphs, text = r->text;
2459 		i < r->len;
2460 		i++
2461 	) {
2462 		uint32_t c = *(text++);
2463 		if ( map8 && c > 128 ) c = map8[c];
2464                	*(glyphs++) = XftCharIndex(DISP, font, c);
2465 	}
2466 	r-> n_glyphs = r->len;
2467 
2468 	if ( r-> advances ) {
2469 		uint16_t *advances;
2470 		for (
2471 			i = 0, glyphs = r->glyphs, advances = r->advances;
2472 			i < r-> len;
2473 			i++, glyphs++, advances++
2474 		) {
2475 			XGlyphInfo glyph;
2476 			FT_UInt ft_index = *glyphs;
2477 			XftGlyphExtents( DISP, font, &ft_index, 1, &glyph);
2478 			*advances = glyph.xOff;
2479 		}
2480 		bzero(r->positions, r->len * 2 * sizeof(int16_t));
2481 	}
2482 
2483         return true;
2484 }
2485 
2486 Bool
prima_xft_text_shaper_bytes(Handle self,PTextShapeRec r)2487 prima_xft_text_shaper_bytes( Handle self, PTextShapeRec r)
2488 {
2489 	return prima_xft_text_shaper( self, r, X(self)-> xft_map8);
2490 }
2491 
2492 Bool
prima_xft_text_shaper_ident(Handle self,PTextShapeRec r)2493 prima_xft_text_shaper_ident( Handle self, PTextShapeRec r)
2494 {
2495 	return prima_xft_text_shaper( self, r, NULL);
2496 }
2497 
2498 #ifdef WITH_HARFBUZZ
2499 Bool
prima_xft_text_shaper_harfbuzz(Handle self,PTextShapeRec r)2500 prima_xft_text_shaper_harfbuzz( Handle self, PTextShapeRec r)
2501 {
2502 	DEFXX;
2503 	Bool ret = true;
2504 	int i, j;
2505 	FT_Face face;
2506 	hb_buffer_t *buf;
2507 	hb_font_t *font;
2508 	hb_glyph_info_t *glyph_info;
2509 	hb_glyph_position_t *glyph_pos;
2510 
2511 	if ( !( face = XftLockFace( XX->font->xft))) /*XXX*/
2512 		return -1;
2513 
2514 	buf = hb_buffer_create();
2515 	hb_buffer_add_utf32(buf, r->text, r->len, 0, -1);
2516 
2517 #if HB_VERSION_MAJOR >= 1
2518 #if HB_VERSION_ATLEAST(1,0,3)
2519 	hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
2520 #endif
2521 #endif
2522 	hb_buffer_set_direction(buf, (r->flags & toRTL) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
2523 /*
2524 	hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
2525 	hb_buffer_set_script (buf, hb_script_from_string ("Arab", -1));
2526 */
2527 	if ( r-> language != NULL )
2528 		hb_buffer_set_language(buf, hb_language_from_string(r->language, -1));
2529 	hb_buffer_guess_segment_properties (buf);
2530 
2531 
2532 	font = hb_ft_font_create(face, NULL);
2533 
2534 	hb_shape(font, buf, NULL, 0);
2535 
2536 	glyph_info = hb_buffer_get_glyph_infos(buf, &r->n_glyphs);
2537 	glyph_pos  = hb_buffer_get_glyph_positions(buf, &r->n_glyphs);
2538 
2539 	for (i = j = 0; i < r->n_glyphs; i++) {
2540 		uint32_t c = glyph_info[i].cluster;
2541 		if ( c > r-> len ) {
2542 			/* something bad happened? */
2543 			warn("harfbuzz shaping asssertion failed: got cluster=%d for strlen=%d", c, r->len);
2544 			guts. use_harfbuzz = false;
2545 			ret = false;
2546 			break;
2547 		}
2548                 r->indexes[i] = c;
2549                 r->glyphs[i]   = glyph_info[i].codepoint;
2550 		if ( glyph_pos ) {
2551 			r->advances[i]    = floor(glyph_pos[i].x_advance / 64.0 + .5);
2552 			r->positions[j++] = floor(glyph_pos[i].x_offset  / 64.0 + .5);
2553 			r->positions[j++] = floor(glyph_pos[i].y_offset  / 64.0 + .5);
2554 		}
2555 	}
2556 
2557 	hb_buffer_destroy(buf);
2558 	hb_font_destroy(font);
2559 	XftUnlockFace(XX->font->xft);
2560 
2561 	return ret;
2562 }
2563 #endif
2564 
2565 static Bool
kill_lists(void * f,int keyLen,void * key,void * dummy)2566 kill_lists( void * f, int keyLen, void * key, void * dummy)
2567 {
2568 	plist_destroy((PList) f);
2569 	return false;
2570 }
2571 
2572 void
prima_xft_init_font_substitution(void)2573 prima_xft_init_font_substitution(void)
2574 {
2575 	int i;
2576 	FcFontSet * s;
2577 	FcPattern   *pat, **ppat;
2578 	FcObjectSet *os;
2579 	PHash core_fonts = hash_create();
2580 	PFontInfo info;
2581 
2582 	for ( i = 0, info = guts. font_info; i < guts. n_fonts; i++, info++) {
2583 		PList list;
2584 		int len = strlen(info->font.name);
2585 		list = (PList) hash_fetch( core_fonts, info-> font.name, len);
2586 		if ( !list ) {
2587 			list = plist_create(32, 32);
2588 			hash_store( core_fonts, info-> font.name, len, (void*) list);
2589 		}
2590 		list_add( list, (Handle) i);
2591 	}
2592 
2593 	if ( guts.default_font_ok) {
2594 		pat = FcPatternCreate();
2595 		FcPatternAddBool( pat, FC_SCALABLE, 1);
2596 		FcPatternAddString( pat, FC_FAMILY, (FcChar8*) guts.default_font.name);
2597 		os = FcObjectSetBuild( FC_FAMILY, (void*) 0);
2598 		s = FcFontList( 0, pat, os);
2599 		if ( s && s->nfont ) {
2600 			PFont f;
2601 			if (( f = prima_font_mapper_save_font(guts.default_font.name, 0)) != NULL ) {
2602 				f->is_utf8 = guts.default_font.is_utf8;
2603 				f->undef.name = 0;
2604 				strncpy(f->family, guts.default_font.family,256);
2605 				f->undef.vector = 0;
2606 				f->vector = guts.default_font.vector;
2607 				f->undef.pitch = 0;
2608 				f->pitch  = guts.default_font.pitch;
2609 			}
2610 		}
2611 		FcObjectSetDestroy( os);
2612 		FcPatternDestroy( pat);
2613 		FcFontSetDestroy(s);
2614 	}
2615 
2616 	pat = FcPatternCreate();
2617 	FcPatternAddBool( pat, FC_SCALABLE, 1);
2618 	os = FcObjectSetBuild( FC_FAMILY, FC_FOUNDRY, FC_SCALABLE, FC_SPACING, FC_WEIGHT, FC_SLANT, (void*) 0);
2619 	s = FcFontList( 0, pat, os);
2620 	FcObjectSetDestroy( os);
2621 	FcPatternDestroy( pat);
2622 
2623 	if ( !s) return;
2624 
2625 	ppat = s-> fonts;
2626 	for ( i = 0; i < s->nfont; i++, ppat++) {
2627 		PFont f;
2628 		int j, slant, weight;
2629 		unsigned int style = 0;
2630 		FcChar8 * s;
2631 		PList list;
2632 		char lower[512], *llower = lower, *lupper;
2633 
2634 		if ( FcPatternGetString(*ppat, FC_FAMILY, 0, &s) != FcResultMatch)
2635 			continue;
2636 
2637 		/* disable the corresponding core font */
2638 		lupper = (char*) s;
2639 		while ( *lupper && (lupper - (char*)s) < 512 )
2640 			*(llower++) = tolower((int)*(lupper++));
2641 		*llower = 0;
2642 		if (( list = (PList) hash_fetch(core_fonts, lower, strlen(lower))) != NULL) {
2643 			for (j = 0; j < list->count; j++) {
2644 				PFontInfo info = guts.font_info + (int) list->items[j];
2645 				info->flags.disabled = 1;
2646 			}
2647 		}
2648 
2649 		if ( FcPatternGetInteger( *ppat, FC_SLANT, 0, &slant) == FcResultMatch) {
2650 			if ( slant == FC_SLANT_ITALIC || slant == FC_SLANT_OBLIQUE)
2651 				style |= fsItalic;
2652 		}
2653 		if ( FcPatternGetInteger( *ppat, FC_WEIGHT, 0, &weight) == FcResultMatch) {
2654 			if ( weight <= FC_WEIGHT_LIGHT )
2655 				style |= fsThin;
2656 			else if ( weight >= FC_WEIGHT_BOLD)
2657 				style |= fsBold;
2658 		}
2659 
2660 		if ( !( f = prima_font_mapper_save_font((const char*) s, style)))
2661 			continue;
2662 
2663 		f-> is_utf8.name = utf8_flag_strncpy( f->name, (char*)s, 255);
2664 		f-> undef.name = 0;
2665 
2666 		if ( FcPatternGetString(*ppat, FC_FOUNDRY, 0, &s) == FcResultMatch) {
2667 			f->is_utf8.family = utf8_flag_strncpy( f->family, (char*)s, 255);
2668 		}
2669 		if ( FcPatternGetInteger(*ppat, FC_SPACING, 0, &j) == FcResultMatch) {
2670 			f->pitch = (( i == FC_PROPORTIONAL) ? fpVariable : fpFixed);
2671 			f->undef.pitch = 0;
2672 		}
2673 
2674 		f->undef.vector = 0;
2675 		f->vector = fvOutline;
2676 	}
2677 
2678 	FcFontSetDestroy(s);
2679 
2680 	hash_first_that( core_fonts, (void*)kill_lists, NULL, NULL, NULL);
2681 	hash_destroy( core_fonts, false);
2682 }
2683 
2684 #else
2685 #error Required: Xft version 2.1.0 or higher; fontconfig version 2.0.1 or higher. To compile without Xft, re-run 'perl Makefile.PL WITH_XFT=0'
2686 #endif /* USE_XFT */
2687