1 /***********************************************************/
2 /*                                                         */
3 /*  System dependent font routines (unix, x11)             */
4 /*                                                         */
5 /***********************************************************/
6 
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include "unix/guts.h"
10 #include <locale.h>
11 
12 static PHash xfontCache = NULL;
13 static Bool have_vector_fonts = false;
14 static PHash encodings = NULL;
15 static char **ignore_encodings;
16 static int n_ignore_encodings;
17 static char *s_ignore_encodings;
18 
19 /* these are freed right after use */
20 static char * do_default_font = NULL;
21 static char * do_caption_font = NULL;
22 static char * do_msg_font = NULL;
23 static char * do_menu_font = NULL;
24 static char * do_widget_font = NULL;
25 static Bool   do_xft = true;
26 static Bool   do_core_fonts = true;
27 static Bool   do_xft_no_antialias = false;
28 static Bool   do_xft_priority = true;
29 static Bool   do_no_scaled_fonts = false;
30 static Bool   do_harfbuzz = true;
31 
32 static void detail_font_info( PFontInfo f, PFont font, Bool addToCache, Bool bySize);
33 
34 static void
str_lwr(char * d,const char * s)35 str_lwr( char *d, const char *s)
36 {
37 	while ( *s) {
38 		*d++ = tolower( *s++);
39 	}
40 	*d = '\0';
41 }
42 
43 static void
fill_default_font(Font * font)44 fill_default_font( Font * font )
45 {
46 	bzero( font, sizeof( Font));
47 	strcpy( font-> name, "Default");
48 	font-> size = 12;
49 	font-> style = fsNormal;
50 	font-> pitch = fpDefault;
51 	font-> vector = fvDefault;
52 	font-> undef. height = font-> undef. width = font-> undef. vector = 1;
53 }
54 
55 /* Extracts font name, charset, foundry etc from X properties, if available.
56 	Usually it is when X server can access its internal font files directly.
57 	This means two things:
58 	- X properties values are not the same as XLFD, and are precise font descriptors
59 	- alias fonts ( defined via fonts.alias ) don't have X properties
60 */
61 static void
font_query_name(XFontStruct * s,PFontInfo f)62 font_query_name( XFontStruct * s, PFontInfo f)
63 {
64 	unsigned long v;
65 	char * c;
66 
67 	if ( !f-> flags. encoding) {
68 		c = NULL;
69 		if ( XGetFontProperty( s, FXA_CHARSET_REGISTRY, &v) && v) {
70 			XCHECKPOINT;
71 			c = XGetAtomName( DISP, (Atom)v);
72 			XCHECKPOINT;
73 			if ( c) {
74 				f-> flags. encoding = true;
75 				str_lwr( f-> font. encoding, c);
76 				XFree( c);
77 			}
78 		}
79 
80 		if ( c) {
81 			c = NULL;
82 			if ( XGetFontProperty( s, FXA_CHARSET_ENCODING, &v) && v) {
83 				XCHECKPOINT;
84 				c = XGetAtomName( DISP, (Atom)v);
85 				XCHECKPOINT;
86 				if ( c) {
87 					strcat( f-> font. encoding, "-");
88 					str_lwr( f-> font. encoding + strlen( f-> font. encoding), c);
89 					XFree( c);
90 				}
91 			}
92 		}
93 
94 		if ( !c) {
95 			f-> flags. encoding = false;
96 			f-> font. encoding[0] = 0;
97 		}
98 	}
99 
100 	/* detailing family */
101 	if ( ! f-> flags. family && XGetFontProperty( s, FXA_FOUNDRY, &v) && v) {
102 		XCHECKPOINT;
103 		c = XGetAtomName( DISP, (Atom)v);
104 		XCHECKPOINT;
105 		if ( c) {
106 			f-> flags. family = true;
107 			strncpy( f-> font. family, c, 255);  f-> font. family[255] = '\0';
108 			str_lwr( f-> font. family, f-> font. family);
109 			XFree( c);
110 		}
111 	}
112 
113 	/* detailing name */
114 	if ( ! f-> flags. name && XGetFontProperty( s, FXA_FAMILY_NAME, &v) && v) {
115 		XCHECKPOINT;
116 		c = XGetAtomName( DISP, (Atom)v);
117 		XCHECKPOINT;
118 		if ( c) {
119 			f-> flags. name = true;
120 			strncpy( f-> font. name, c, 255);  f-> font. name[255] = '\0';
121 			str_lwr( f-> font. name, f-> font. name);
122 			XFree( c);
123 		}
124 	}
125 
126 	if ( ! f-> flags. family && ! f-> flags. name) {
127 		c = f-> xname;
128 		if ( strchr( c, '-') == NULL) {
129 			strcpy( f-> font. name, c);
130 			strcpy( f-> font. family, c);
131 		} else {
132 			char * d = c;
133 			int cnt = 0, lim;
134 			if ( *d == '-') d++;
135 			while ( *(c++)) {
136 				if ( *c == '-' || *(c + 1)==0) {
137 					if ( c == d ) continue;
138 					if ( cnt == 0 ) {
139 						lim = ( c - d > 255 ) ? 255 : c - d;
140 						strncpy( f-> font. family, d, lim);
141 						cnt++;
142 					} else if ( cnt == 1) {
143 						lim = ( c - d > 255 ) ? 255 : c - d;
144 						strncpy( f-> font. name, d, lim);
145 						break;
146 					} else
147 						break;
148 					d = c + 1;
149 				}
150 			}
151 
152 			if (( strlen( f-> font. family) == 0) || (strcmp( f-> font. family, "*") == 0))
153 				strcpy( f-> font. family, guts. default_font. family);
154 			if (( strlen( f-> font. name) == 0) || (strcmp( f-> font. name, "*") == 0)) {
155 				if ( guts. default_font_ok) {
156 					strcpy( f-> font. name, guts. default_font. name);
157 				} else {
158 					Font fx = f-> font;
159 					fill_default_font( &fx);
160 					if ( f-> flags. encoding) strcpy( fx. encoding, f-> font. encoding);
161 					prima_core_font_pick( application, &fx, &fx);
162 					strcpy( f-> font. name, fx. name);
163 				}
164 			} else {
165 				char c[512];
166 				snprintf( c, 512, "%s %s", f-> font. family, f-> font. name);
167 				strncpy( f-> font. name, c, 256);
168 				f-> font.name[255] = 0;
169 			}
170 		}
171 		str_lwr( f-> font. family, f-> font. family);
172 		str_lwr( f-> font. name, f-> font. name);
173 		f-> flags. name = true;
174 		f-> flags. family = true;
175 	} else if ( ! f-> flags. family ) {
176 		str_lwr( f-> font. family, f-> font. name);
177 		f-> flags. name = true;
178 	} else if ( ! f-> flags. name ) {
179 		str_lwr( f-> font. name, f-> font. family);
180 		f-> flags. name = true;
181 	}
182 }
183 
184 static Bool
xlfd_parse_font(char * xlfd_name,PFontInfo info,Bool do_vector_fonts)185 xlfd_parse_font( char * xlfd_name, PFontInfo info, Bool do_vector_fonts)
186 {
187 	char *b, *t, *c = xlfd_name;
188 	int nh = 0;
189 	Bool conformant = 0;
190 	int style = 0;    /* must become 2 if we know it */
191 	int vector = 0;   /* must become 5, or 3 if we know it */
192 
193         bzero( &info-> font, sizeof(info->font));
194         bzero( &info-> flags, sizeof(info->flags));
195 
196 	info-> flags. sloppy = true;
197 
198 	/*
199 	* The code below tries to deduce several values from the name
200 	* of a font, which cannot be relied upon (as specified by XLFD).
201 	*
202 	* Recognizing the bad side of such practice, I cannot think of any
203 	* other way to get certain font characteristics we need without
204 	* loading the font information, which is prohibitively expensive
205 	* here due to enumeration of all the fonts in the system.
206 	*/
207 
208 	while (*c) if ( *c++ == '-') nh++;
209 	c = xlfd_name;
210 	if ( nh == 14) {
211 		if ( *c == '+') while (*c && *c != '-')  c++;	    /* skip VERSION */
212 		/* from now on *c == '-' is true (on this level) for all valid XLFD names */
213 		t = info-> font. family;
214 		if ( *c == '-') {
215 			/* advance through FOUNDRY */
216 			++c;
217 			while ( *c && *c != '-') { *t++ = *c++; }
218 			*t++ = '\0';
219 		}
220 		if ( *c == '-') {
221 			/* advance through FAMILY_NAME */
222 			++c;  b = t;
223 			while ( *c && *c != '-') { *t++ = *c++; }
224 			info-> name_offset = c - xlfd_name;
225 			*t = '\0';
226 			info-> flags. name = true;
227 			info-> flags. family = true;
228 			strcpy( info-> font. name, b);
229 
230 			if (
231 				( info-> font.family[0] == '*' && info-> font.family[1] == 0)  ||
232 				( b[0] == '*' && b[1] == 0)
233 			) {
234 				Font xf;
235 				int noname =  ( b[0] == '*' && b[1] == 0);
236 				int nofamily = ( info-> font.family[0] == '*' && info-> font.family[1] == 0);
237 				if ( guts. default_font_ok)
238 					xf = guts. default_font;
239 				else
240 					fill_default_font( &xf);
241 				if ( !nofamily) strcpy( xf. family, info-> font. family);
242 				if ( !noname)   strcpy( xf. name, info-> font. name);
243 				prima_core_font_pick( NULL_HANDLE, &xf, &xf);
244 				if ( noname)   strcpy( info-> font. name,   xf. name);
245 				if ( nofamily) strcpy( info-> font. family, xf. family);
246 			}
247 			if (
248 				( strcmp( info->font.name, "clean") == 0) ||
249 				( strcmp( info->font.name, "fixed") == 0) ||
250 				( strcmp( info->font.name, "bitstream charter") == 0)
251 			)
252 				info-> flags. known = 1;
253 		}
254 
255 		if ( *c == '-') {
256 			/* advance through WEIGHT_NAME */
257 			b = ++c;
258 			while ( *c && *c != '-') c++;
259 			if (
260 				c-b == 0 ||
261 		 		(c-b == 6 && strncasecmp( b, "medium", 6) == 0) ||
262 		 		(c-b == 7 && strncasecmp( b, "regular", 7) == 0)
263 			) {
264 				info-> font. style = fsNormal;
265 				style++;
266 				info-> font. weight = fwMedium;
267 				info-> flags. weight = true;
268 			} else if ( c-b == 4 && strncasecmp( b, "bold", 4) == 0) {
269 				info-> font. style = fsBold;
270 				style++;
271 				info-> font. weight = fwBold;
272 				info-> flags. weight = true;
273 			} else if ( c-b == 8 && strncasecmp( b, "demibold", 8) == 0) {
274 				info-> font. style = fsBold;
275 				style++;
276 				info-> font. weight = fwSemiBold;
277 				info-> flags. weight = true;
278 			} else if ( c-b == 1 && *b == '*') {
279 				info-> font. style  = fsNormal;
280 				style++;
281 				info-> font. weight = fwMedium;
282 				info-> flags. weight = true;
283 			}
284 		}
285 		if ( *c == '-') {
286 			/* advance through SLANT */
287 			b = ++c;
288 			while ( *c && *c != '-') c++;
289 			if ( c-b == 1 && (*b == 'R' || *b == 'r')) {
290 				style++;
291 			} else if ( c-b == 1 && (*b == 'I' || *b == 'i')) {
292 				info-> font. style |= fsItalic;
293 				style++;
294 			} else if ( c-b == 1 && (*b == 'O' || *b == 'o')) {
295 				info-> font. style |= fsItalic;   /* XXX Oblique? */
296 				style++;
297 			} else if ( c-b == 2 && (*b == 'R' || *b == 'r') && (b[1] == 'I' || b[1] == 'i')) {
298 				info-> font. style |= fsItalic;   /* XXX Reverse Italic? */
299 				style++;
300 			} else if ( c-b == 2 && (*b == 'R' || *b == 'r') && (b[1] == 'O' || b[1] == 'o')) {
301 				info-> font. style |= fsItalic;   /* XXX Reverse Oblique? */
302 				style++;
303 			}
304 		}
305 		if ( *c == '-') {
306 			/* advance through SETWIDTH_NAME */
307 			b = ++c;
308 			while ( *c && *c != '-') c++;
309 			if ( c-b == 9 && strncasecmp( b, "condensed", 9) == 0) {
310 				info-> font. style |= fsThin;
311 				style++;
312 			}
313 		}
314 		if ( *c == '-') {
315 			/* advance through ADD_STYLE_NAME; just skip it;  XXX */
316 			++c;
317 			while ( *c && *c != '-') c++;
318 		}
319 		if ( *c == '-') {
320 			/* advance through PIXEL_SIZE */
321 			c++; b = c;
322 			if ( *c != '-')
323 				info-> font. height = strtol( c, &b, 10);
324 			if ( c != b) {
325 				if ( info-> font. height) {
326 					info-> flags. height = true;
327 				} else {
328 					vector++;
329 				}
330 				c = b;
331 			} else if ( strncmp( c, "*-", 2) == 0) c++;
332 		}
333 		if ( *c == '-') {
334 			/* advance through POINT_SIZE */
335 			c++; b = c;
336 			if ( *c != '-')
337 				info-> font. size = strtol( c, &b, 10);
338 			if ( c != b) {
339 				if ( info-> font. size) {
340 					info-> flags. size = true;
341 					info-> font. size  = ( info-> font. size < 10) ?
342 						1 : ( info-> font. size / 10);
343 				} else {
344 					vector++;
345 				}
346 				c = b;
347 			} else if ( strncmp( c, "*-", 2) == 0) c++;
348 		}
349 		if ( *c == '-') {
350 			/* advance through RESOLUTION_X */
351 			c++; b = c;
352 			if ( *c != '-')
353 				info-> font. xDeviceRes = strtol( c, &b, 10);
354 			if ( c != b) {
355 				if ( info-> font. xDeviceRes) {
356 					info-> flags. xDeviceRes = true;
357 				} else {
358 					vector++;
359 				}
360 				c = b;
361 			} else if ( strncmp( c, "*-", 2) == 0) c++;
362 		}
363 		if ( *c == '-') {
364 			/* advance through RESOLUTION_Y */
365 			c++; b = c;
366 			if ( *c != '-')
367 				info-> font. yDeviceRes = strtol( c, &b, 10);
368 			if ( c != b) {
369 				if ( info-> font. yDeviceRes) {
370 					info-> flags. yDeviceRes = true;
371 				} else {
372 					vector++;
373 				}
374 				c = b;
375 			} else if ( strncmp( c, "*-", 2) == 0) c++;
376 		}
377 		if ( *c == '-') {
378 			/* advance through SPACING */
379 			b = ++c;
380 			while ( *c && *c != '-') c++;
381 			if ( c - b == 1) {
382 				if ( strchr( "pP", *b)) {
383 					info-> font. pitch = fpVariable;
384 					info-> flags. pitch = true;
385 				} else if ( strchr( "mMcC", *b)) {
386 					info-> font. pitch = fpFixed;
387 					info-> flags. pitch = true;
388 				} else if ( *b == '*') {
389 					info-> font. pitch = fpDefault;
390 					info-> flags. pitch = true;
391 				}
392 			}
393 		}
394 		if ( *c == '-') {
395 			/* advance through AVERAGE_WIDTH */
396 			c++; b = c;
397 			if ( *c != '-')
398 				info-> font. width = strtol( c, &b, 10);
399 			if ( c != b) {
400 				if ( info-> font. width) {
401 					info-> flags. width = true;
402 					info-> font. width  = ( info-> font. width < 10) ?
403 						1 : ( info-> font. width / 10);
404 				} else {
405 					vector++;
406 				}
407 				c = b;
408 			} else if ( strncmp( c, "*-", 2) == 0) c++;
409 		}
410 		if ( *c == '-') {
411 			/* advance through CHARSET_REGISTRY;  */
412 			++c;
413 			info-> info_offset = c - xlfd_name;
414 			if ( strchr( c, '*') == NULL) {
415 				info-> flags. encoding = 1;
416 				strcpy( info-> font. encoding, c);
417 				hash_store( encodings, c, strlen( c), (void*)1);
418 			} else
419 				info-> font. encoding[0] = 0;
420 			if (
421 				( strncasecmp( c, "sunolglyph",  strlen("sunolglyph")) == 0) ||
422 				( strncasecmp( c, "sunolcursor", strlen("sunolcursor")) == 0) ||
423 				( strncasecmp( c, "misc",        strlen("misc")) == 0)
424 				)
425 				info-> flags. funky = 1;
426 
427 			while ( *c && *c != '-') c++;
428 		}
429 		if ( *c == '-') {
430 			int m;
431 			c++;
432 			for (m = 0; m < n_ignore_encodings; m++) {
433 				if (strcmp(c, ignore_encodings[m]) == 0)
434 					goto skip_font;
435 			}
436 			if (
437 				(strncmp( c, "0",  strlen("0")) == 0) ||
438 				(strncmp( c, "fontspecific", strlen("fontspecific")) == 0) ||
439 				(strncmp( c, "special", strlen("special")) == 0)
440 				)
441 				info-> flags. funky = 1;
442 
443 			/* advance through CHARSET_ENCODING; just skip it;  XXX */
444 			while ( *c && *c != '-') c++;
445 			if ( !*c && info-> flags. pitch &&
446 		      ( !do_vector_fonts || vector == 5 || vector == 3 ||
447 				( info-> flags. height &&
448 					info-> flags. size &&
449 					info-> flags. xDeviceRes &&
450 					info-> flags. yDeviceRes &&
451 					info-> flags. width))) {
452 				conformant = true;
453 				if ( style == 2) info-> flags. style = true;
454 
455 				if ( do_vector_fonts && ( vector == 5 || vector == 3)) {
456 					char pattern[ 1024], *pat = pattern;
457 					int dash = 0;
458 					info-> font. vector = fvScalableBitmap;
459 					if ( guts. no_scaled_fonts) info-> flags. disabled = true;
460 					info-> flags. bad_vector = (vector == 3);
461 
462 					c = xlfd_name;
463 					while (*c) {
464 						if ( *c == '%') {
465 							*pat++ = *c;
466 							*pat++ = *c++;
467 						} else if ( *c == '-') {
468 							dash++;
469 							*pat++ = *c++;
470 							switch ( dash) {
471 							case 9: case 10:
472 								if ( vector == 3)
473 									break;
474 							case 7: case 8: case 12:
475 								*pat++ = '%';
476 								*pat++ = 'd';
477 								while (*c && *c != '-') c++;
478 								break;
479 							}
480 						} else {
481 							*pat++ = *c++;
482 						}
483 					}
484 					*pat++ = '\0';
485 					if (( info-> vecname = malloc( pat - pattern)))
486 						strcpy( info-> vecname, pattern);
487 				} else
488 					info-> font. vector = fvBitmap;
489 				info-> flags. vector = true;
490 			}
491 		}
492 	}
493 skip_font:
494 	return conformant;
495 }
496 
497 static Bool
pick_default_font_with_encoding(void)498 pick_default_font_with_encoding(void)
499 {
500 	PFontInfo info;
501 	int i, best = -1, best_weight = 0, max_weight = 5;
502 
503 	if ( !guts. no_scaled_fonts) max_weight++;
504 	for ( i = 0, info = guts. font_info; i < guts. n_fonts; i++, info++) {
505 		if ( strcmp( info-> font. encoding, guts. locale) == 0) {
506 			int weight = 0;
507 			if ( info-> font. style == fsNormal) weight++;
508 			if ( info-> font. weight == fwMedium) weight++;
509 			if ( info-> font. pitch == fpVariable) weight++;
510 			if ( info-> font. vector > fvBitmap) weight++;
511 			if (
512 				( strcmp( info-> font.name, "helvetica") == 0 ) ||
513 				( strcmp( info-> font.name, "arial") == 0 )
514 			)
515 				weight+=2;
516 			if (
517 				( strcmp( info-> font.name, "lucida") == 0 ) ||
518 				( strcmp( info-> font.name, "verdana") == 0 )
519 			)
520 				weight++;
521 			if ( weight > best_weight) {
522 				best_weight = weight;
523 				best = i;
524 				if ( weight == max_weight) break;
525 			}
526 		}
527 	}
528 
529 	if ( best >= 0) {
530 		fill_default_font( &guts. default_font);
531 		strcpy( guts. default_font. name, guts. font_info[best].font.name);
532 		strcpy( guts. default_font. encoding, guts. locale);
533 		prima_core_font_pick( application, &guts. default_font, &guts. default_font);
534 		guts. default_font. pitch = fpDefault;
535 		return true;
536 	}
537 	return false;
538 }
539 
540 Bool
prima_init_font_subsystem(char * error_buf)541 prima_init_font_subsystem( char * error_buf)
542 {
543 	char **names;
544 	int count, j , i, bad_fonts = 0, vector_fonts = 0;
545 	PFontInfo info;
546 
547 	if ( do_core_fonts) {
548 		int nocorefonts = 0;
549 		apc_fetch_resource( "Prima", "", "Nocorefonts", "nocorefonts",
550 			NULL_HANDLE, frUnix_int, &nocorefonts);
551 		if ( nocorefonts) do_core_fonts = false;
552 	}
553 
554 	guts. font_names = names = XListFonts( DISP,
555 		do_core_fonts ? "*" : "-*-fixed-*",
556 		INT_MAX, &count);
557 	if ( !names) {
558 		sprintf( error_buf, "No fonts returned by XListFonts, cannot continue");
559 		return false;
560 	}
561 
562 	info = malloc( sizeof( FontInfo) * count);
563 	if ( !info) {
564 		sprintf( error_buf, "No memory (%d bytes)", (int)sizeof(FontInfo)*count);
565 		return false;
566 	}
567 	bzero( info,  sizeof( FontInfo) * count);
568 
569 	n_ignore_encodings = 0;
570 	ignore_encodings = NULL;
571 	s_ignore_encodings = NULL;
572 	if ( apc_fetch_resource( "Prima", "", "IgnoreEncodings", "ignoreEncodings",
573 				NULL_HANDLE, frString, &s_ignore_encodings))
574 	{
575 		char *e = s_ignore_encodings;
576 		char *s = e;
577 		while (*e) {
578 			while (*e && *e != ';') {
579 				e++;
580 			}
581 			if (*e == ';') {
582 				n_ignore_encodings++;
583 				*e = '\0';
584 				s = ++e;
585 			} else if (e > s) {
586 				n_ignore_encodings++;
587 			}
588 		}
589 		ignore_encodings = malloc( sizeof( char*) * n_ignore_encodings);
590 		if ( !ignore_encodings) {
591 			sprintf( error_buf, "No memory");
592 			return false;
593 		}
594 		bzero( ignore_encodings,  sizeof( char*) * n_ignore_encodings);
595 		s = s_ignore_encodings;
596 		for (i = 0; i < n_ignore_encodings; i++) {
597 			ignore_encodings[i] = s;
598 			while (*s) s++;
599 			s++;
600 		}
601 	}
602 
603 	encodings = hash_create();
604 
605 	apc_fetch_resource( "Prima", "", "Noscaledfonts", "noscaledfonts",
606 		NULL_HANDLE, frUnix_int, &guts. no_scaled_fonts);
607 	if ( do_no_scaled_fonts) guts. no_scaled_fonts = 1;
608 
609 	for ( i = 0, j = 0; i < count; i++) {
610 		if ( xlfd_parse_font( names[i], info + j, true)) {
611 			vector_fonts += ( info[j]. font. vector == fvBitmap ) ? 0 : 1;
612 			info[j]. xname = names[ i];
613 			j++;
614 		} else
615 			bad_fonts++;
616 	}
617 
618 	guts. font_info = info;
619 	guts. n_fonts = j;
620 	if ( vector_fonts > 0) have_vector_fonts = true;
621 
622 	guts. font_hash = hash_create();
623 	xfontCache      = hash_create();
624 
625 	/* locale */
626 	{
627 		int len;
628 		char * s = setlocale( LC_CTYPE, NULL);
629 		while ( *s && *s != '.') s++;
630 		while ( *s && *s == '.') s++;
631 		strncpy( guts. locale, s, 31);
632 		guts. locale[31] = 0;
633 		len = strlen( guts. locale);
634 		if ( !hash_fetch( encodings, guts. locale, len)) {
635 			str_lwr( guts. locale, guts. locale);
636 			if ( !hash_fetch( encodings, guts. locale, len) &&
637 				(
638 					( strncmp( guts. locale, "iso-", 4) == 0)||
639 					( strncmp( guts. locale, "iso_", 4) == 0)
640 				)
641 				) {
642 				s = guts. locale + 3;
643 				while ( *s) {
644 					*s = s[1];
645 					s++;
646 				}
647 				if ( !hash_fetch( encodings, guts. locale, len - 1))
648 					guts. locale[0] = 0;
649 			}
650 		}
651 		if ( strcmp( guts. locale, "utf-8" ) == 0 )
652 			strcpy( guts. locale, "iso10646-1");
653 	}
654 
655 	guts. xft_no_antialias = do_xft_no_antialias;
656 	guts. xft_priority     = do_xft_priority;
657 #ifdef USE_XFT
658 	if ( do_xft) prima_xft_init();
659 #endif
660 	guts. use_harfbuzz     = do_xft && do_harfbuzz;
661 
662 	prima_font_pp2font( "fixed", NULL);
663 	Fdebug("font: init\n");
664 	if ( do_default_font) {
665 		XrmPutStringResource( &guts.db, "Prima.font", do_default_font);
666 		prima_font_pp2font( do_default_font, &guts. default_font);
667 		free( do_default_font);
668 		do_default_font = NULL;
669 	} else if ( !apc_fetch_resource( "Prima", "", "Font", "font",
670 		NULL_HANDLE, frFont, &guts. default_font)) {
671 		fill_default_font( &guts. default_font);
672 		apc_font_pick( application, &guts. default_font, &guts. default_font);
673 		guts. default_font. pitch = fpDefault;
674 		/*
675 			Although apc_font_pick() does respect $LANG, it does not always picks
676 			up a font with the correct encoding here, because we use a hard-coded
677 			string "Default". Whereas users can set an alias for "Default",
678 			or set the default font via XRDB:Prima.font option, it is not done by
679 			default, so here we pick such a font that contains the user-specified
680 			encoding, and has more or less reasonable metrics.
681 		*/
682 		if ( guts. locale[0] && (strcmp( guts. locale, guts. default_font. encoding) != 0)) {
683 			pick_default_font_with_encoding();
684 		}
685 	}
686 	guts. default_font_ok = 1;
687 #define DEBUG_FONT(font) font.height,font.width,font.size,font.name,font.encoding
688 	Fdebug("default font: %d.[w=%d,s=%d].%s.%s\n", DEBUG_FONT(guts.default_font));
689 	if ( do_menu_font) {
690 		XrmPutStringResource( &guts.db, "Prima.menu_font", do_menu_font);
691 		prima_font_pp2font( do_menu_font, &guts. default_menu_font);
692 		free( do_menu_font);
693 		do_menu_font = NULL;
694 	} else if ( !apc_fetch_resource( "Prima", "", "Font", "menu_font",
695 		NULL_HANDLE, frFont, &guts. default_menu_font)) {
696 		memcpy( &guts. default_menu_font, &guts. default_font, sizeof( Font));
697 	}
698 	Fdebug("menu font: %d.[w=%d,s=%d].%s.%s\n", DEBUG_FONT(guts.default_menu_font));
699 
700 	if ( do_widget_font) {
701 		XrmPutStringResource( &guts.db, "Prima.widget_font", do_widget_font);
702 		prima_font_pp2font( do_widget_font, &guts. default_widget_font);
703 		free( do_widget_font);
704 		do_widget_font = NULL;
705 	} else if ( !apc_fetch_resource( "Prima", "", "Font", "widget_font",
706 		NULL_HANDLE, frFont, &guts. default_widget_font)) {
707 		memcpy( &guts. default_widget_font, &guts. default_font, sizeof( Font));
708 	}
709 	Fdebug("widget font: %d.[w=%d,s=%d].%s.%s\n", DEBUG_FONT(guts.default_widget_font));
710 
711 	if ( do_msg_font) {
712 		XrmPutStringResource( &guts.db, "Prima.message_font", do_widget_font);
713 		prima_font_pp2font( do_msg_font, &guts. default_msg_font);
714 		free( do_msg_font);
715 		do_msg_font = NULL;
716 	} else if ( !apc_fetch_resource( "Prima", "", "Font", "message_font",
717 		NULL_HANDLE, frFont, &guts. default_msg_font)) {
718 		memcpy( &guts. default_msg_font, &guts. default_font, sizeof( Font));
719 	}
720 	Fdebug("msg font: %d.[w=%d,s=%d].%s.%s\n", DEBUG_FONT(guts.default_msg_font));
721 
722 	if ( do_caption_font) {
723 		XrmPutStringResource( &guts.db, "Prima.caption_font", do_widget_font);
724 		prima_font_pp2font( do_caption_font, &guts. default_caption_font);
725 		free( do_caption_font);
726 		do_caption_font = NULL;
727 	} else if ( !apc_fetch_resource( "Prima", "", "Font", "caption_font",
728 		NULL_HANDLE, frFont, &guts. default_caption_font)) {
729 		memcpy( &guts. default_caption_font, &guts. default_font, sizeof( Font));
730 	}
731 	Fdebug("caption font: %d.[w=%d,s=%d].%s.%s\n", DEBUG_FONT(guts.default_caption_font));
732 #ifdef USE_XFT
733 	if ( do_xft) prima_xft_init_font_substitution();
734 #endif
735 
736 	return true;
737 }
738 
739 Bool
prima_font_subsystem_set_option(char * option,char * value)740 prima_font_subsystem_set_option( char * option, char * value)
741 {
742 	if ( strcmp( option, "no-core-fonts") == 0) {
743 		if ( value) warn("`--no-core' option has no parameters");
744 		do_core_fonts = false;
745 		return true;
746 	} else
747 #ifdef USE_XFT
748 	if ( strcmp( option, "no-xft") == 0) {
749 		if ( value) warn("`--no-xft' option has no parameters");
750 		do_xft = false;
751 		return true;
752 	} else
753 	if ( strcmp( option, "no-aa") == 0) {
754 		if ( value) warn("`--no-antialias' option has no parameters");
755 		do_xft_no_antialias = true;
756 		return true;
757 	} else
758 	if ( strcmp( option, "font-priority") == 0) {
759 		if ( !value) {
760 			warn("`--font-priority' must be given parameters, either 'core' or 'xft'");
761 			return false;
762 		}
763 		if ( strcmp( value, "core") == 0)
764 			do_xft_priority = false;
765 		else if ( strcmp( value, "xft") == 0)
766 			do_xft_priority = true;
767 		else
768 			warn("Invalid value '%s' to `--font-priority' option. Valid are 'core' and 'xft'", value);
769 		return true;
770 	} else
771 #endif
772 #ifdef WITH_HARFBUZZ
773 	if ( strcmp( option, "no-harfbuzz") == 0) {
774 		if ( value) warn("`--no-harfbuzz' option has no parameters");
775 		do_harfbuzz = false;
776 		return true;
777 	} else
778 #endif
779 	if ( strcmp( option, "noscaled") == 0) {
780 		if ( value) warn("`--noscaled' option has no parameters");
781 		do_no_scaled_fonts = true;
782 		return true;
783 	} else
784 	if ( strcmp( option, "font") == 0) {
785 		free( do_default_font);
786 		do_default_font = duplicate_string( value);
787 		Mdebug( "set default font: %s\n", do_default_font);
788 		return true;
789 	} else
790 	if ( strcmp( option, "menu-font") == 0) {
791 		free( do_menu_font);
792 		do_menu_font = duplicate_string( value);
793 		Mdebug( "set menu font: %s\n", do_menu_font);
794 		return true;
795 	} else
796 	if ( strcmp( option, "widget-font") == 0) {
797 		free( do_widget_font);
798 		do_widget_font = duplicate_string( value);
799 		Mdebug( "set menu font: %s\n", do_widget_font);
800 		return true;
801 	} else
802 	if ( strcmp( option, "msg-font") == 0) {
803 		free( do_msg_font);
804 		do_msg_font = duplicate_string( value);
805 		Mdebug( "set msg font: %s\n", do_msg_font);
806 		return true;
807 	} else
808 	if ( strcmp( option, "caption-font") == 0) {
809 		free( do_caption_font);
810 		do_caption_font = duplicate_string( value);
811 		Mdebug( "set caption font: %s\n", do_caption_font);
812 		return true;
813 	}
814 	return false;
815 }
816 
817 void
prima_font_pp2font(char * ppFontNameSize,PFont font)818 prima_font_pp2font( char * ppFontNameSize, PFont font)
819 {
820 	int i, newEntry = 0, len, dash = 0;
821 	FontInfo fi;
822 	XFontStruct * xf;
823 	Font dummy, def_dummy, *def;
824 	char buf[512], *p;
825 
826 	if ( !font) font = &dummy;
827 
828 
829 	/* check if font is XLFD and ends in -*-*, so we can replace it with $LANG */
830 	len = strlen( ppFontNameSize);
831 	for ( i = 0, p = ppFontNameSize; i < len; i++, p++)
832 		if ( *p == '-') dash++;
833 	if (( dash == 14) && guts. locale[0] && (strcmp( ppFontNameSize + len - 4, "-*-*") == 0)) {
834 		memcpy( buf, ppFontNameSize, len - 3);
835 		buf[ len - 3] = 0;
836 		strncat( buf, guts. locale, 512 - strlen(buf) - 1);
837 		buf[511] = 0;
838 		ppFontNameSize = buf;
839 		len = strlen( ppFontNameSize);
840 	}
841 
842 	/* check if the parsed font already present */
843 	memset( font, 0, sizeof( Font));
844 	for ( i = 0; i < guts. n_fonts; i++) {
845 		if ( strcmp( guts. font_info[i]. xname, ppFontNameSize) == 0) {
846 			*font = guts. font_info[i]. font;
847 			return;
848 		}
849 	}
850 
851 	xf = ( XFontStruct * ) hash_fetch( xfontCache, ppFontNameSize, len);
852 
853 	if ( !xf ) {
854 		xf = XLoadQueryFont( DISP, ppFontNameSize);
855 		if ( !xf) {
856 			Fdebug("font: cannot load %s\n", ppFontNameSize);
857 			if ( !guts. default_font_ok) {
858 				fill_default_font( font);
859 				apc_font_pick( application, font, font);
860 				font-> pitch = fpDefault;
861 			}
862 #ifdef USE_XFT
863 			if ( !guts. use_xft || !prima_xft_parse( ppFontNameSize, font))
864 #endif
865 				if ( font != &guts. default_font)
866 					*font = guts. default_font;
867 			return;
868 		}
869 		hash_store( xfontCache, ppFontNameSize, len, xf);
870 		newEntry = 1;
871 	}
872 
873 	bzero( &fi, sizeof( fi));
874 	fi. flags. sloppy = true;
875 	fi. xname = ppFontNameSize;
876 	xlfd_parse_font( ppFontNameSize, &fi, false);
877 	font_query_name( xf, &fi);
878 	detail_font_info( &fi, font, false, false);
879 	*font = fi. font;
880 	if ( guts. default_font_ok) {
881 		def = &guts. default_font;
882 	} else {
883 		fill_default_font( def = &def_dummy);
884 		apc_font_pick( application, def, def);
885 	}
886 	if ( font-> height == 0) font-> height = def-> height;
887 	if ( font-> size   == 0) font-> size   = def-> size;
888 	if ( strlen( font-> name) == 0) strcpy( font-> name, def-> name);
889 	if ( strlen( font-> family) == 0) strcpy( font-> family, def-> family);
890 	apc_font_pick( application, font, font);
891 	if (
892 		( stricmp( font-> family, fi. font. family) == 0) &&
893 		( stricmp( font-> name, fi. font. name) == 0)
894 		) newEntry = 0;
895 
896 	if ( newEntry ) {
897 		PFontInfo n = realloc( guts. font_info, sizeof( FontInfo) * (guts. n_fonts + 1));
898 		if ( n) {
899 			guts. font_info = n;
900 			fi. font = *font;
901 			guts. font_info[ guts. n_fonts++] = fi;
902 		}
903 	}
904 	Fdebug("font: %s%s parsed to: %d.[w=%d,s=%d].%s.%s\n",
905 			newEntry ? "new " : "", ppFontNameSize, DEBUG_FONT((*font)));
906 }
907 
908 void
prima_free_rotated_entry(PCachedFont f)909 prima_free_rotated_entry( PCachedFont f)
910 {
911 	while ( f-> rotated) {
912 		PRotatedFont r = f-> rotated;
913 		while ( r-> length--) {
914 			if ( r-> map[ r-> length]) {
915 				prima_free_ximage( r-> map[ r-> length]);
916 				r-> map[ r-> length] = NULL;
917 			}
918 		}
919 		f-> rotated = ( PRotatedFont) r-> next;
920 		XFreeGC( DISP, r-> arena_gc);
921 		if ( r-> arena)
922 			XFreePixmap( DISP, r-> arena);
923 		if ( r-> arena_bits)
924 			free( r-> arena_bits);
925 		free( r);
926 	}
927 }
928 
929 static Bool
free_rotated_entries(PCachedFont f,int keyLen,void * key,void * dummy)930 free_rotated_entries( PCachedFont f, int keyLen, void * key, void * dummy)
931 {
932 	prima_free_rotated_entry( f);
933 	free( f);
934 	return false;
935 }
936 
937 void
prima_cleanup_font_subsystem(void)938 prima_cleanup_font_subsystem( void)
939 {
940 	int i;
941 
942 	if ( guts. font_names)
943 		XFreeFontNames( guts. font_names);
944 	if ( guts. font_info) {
945 		for ( i = 0; i < guts. n_fonts; i++)
946 			if ( guts. font_info[i]. vecname)
947 				free( guts. font_info[i]. vecname);
948 		free( guts. font_info);
949 	}
950 	guts. font_names = NULL;
951 	guts. n_fonts = 0;
952 	guts. font_info = NULL;
953 
954 	free(ignore_encodings);
955 	free(s_ignore_encodings);
956 
957 	if ( guts. font_hash) {
958 		hash_first_that( guts. font_hash, (void*)free_rotated_entries, NULL, NULL, NULL);
959 		hash_destroy( guts. font_hash, false);
960 		guts. font_hash = NULL;
961 	}
962 
963 	hash_destroy( xfontCache, false);
964 	xfontCache = NULL;
965 	hash_destroy( encodings, false);
966 	encodings = NULL;
967 #ifdef USE_XFT
968 	prima_xft_done();
969 #endif
970 
971 }
972 
973 PFont
apc_font_default(PFont f)974 apc_font_default( PFont f)
975 {
976 	memcpy( f, &guts. default_font, sizeof( Font));
977 	return f;
978 }
979 
980 int
apc_font_load(Handle self,char * filename)981 apc_font_load( Handle self, char* filename)
982 {
983 #ifdef USE_XFT
984 	return prima_xft_load_font(filename);
985 #else
986 	return 0;
987 #endif
988 }
989 
990 static void
dump_font(PFont f)991 dump_font( PFont f)
992 {
993 	if ( !DISP) return;
994 	fprintf( stderr, "*** BEGIN FONT DUMP ***\n");
995 	fprintf( stderr, "height: %d\n", f-> height);
996 	fprintf( stderr, "width: %d\n", f-> width);
997 	fprintf( stderr, "style: %d\n", f-> style);
998 	fprintf( stderr, "pitch: %d\n", f-> pitch);
999 	fprintf( stderr, "direction: %g\n", f-> direction);
1000 	fprintf( stderr, "name: %s\n", f-> name);
1001 	fprintf( stderr, "family: %s\n", f-> family);
1002 	fprintf( stderr, "size: %d\n", f-> size);
1003 	fprintf( stderr, "*** END FONT DUMP ***\n");
1004 }
1005 
1006 void
prima_build_font_key(PFontKey key,PFont f,Bool bySize)1007 prima_build_font_key( PFontKey key, PFont f, Bool bySize)
1008 {
1009 	bzero( key, sizeof( FontKey));
1010 	key-> height = bySize ? -f-> size : f-> height;
1011 	key-> width = f-> width;
1012 	key-> style = f-> style & ~(fsUnderlined|fsOutline|fsStruckOut) & fsMask;
1013 	key-> pitch = f-> pitch & fpMask;
1014 	key-> direction = 0;
1015 	strcpy( key-> name, f-> name);
1016 	strcat( key-> name, "\1");
1017 	strcat( key-> name, f-> encoding);
1018 }
1019 
1020 PCachedFont
prima_find_known_font(PFont font,Bool refill,Bool bySize)1021 prima_find_known_font( PFont font, Bool refill, Bool bySize)
1022 {
1023 	FontKey key;
1024 	PCachedFont kf;
1025 
1026 	prima_build_font_key( &key, font, bySize);
1027 	kf = hash_fetch( guts. font_hash, &key, sizeof( FontKey));
1028 	if ( kf && refill) {
1029 		memcpy( font, &kf-> font, sizeof( Font));
1030 	}
1031 	return kf;
1032 }
1033 
1034 static Bool
add_font_to_cache(PFontKey key,PFontInfo f,const char * name,XFontStruct * s,int uPos,int uThinkness)1035 add_font_to_cache( PFontKey key, PFontInfo f, const char *name, XFontStruct *s, int uPos, int uThinkness)
1036 {
1037 	PCachedFont kf;
1038 
1039 	kf = malloc( sizeof( CachedFont));
1040 	if (!kf) {
1041 		warn( "no memory");
1042 		return false;
1043 	}
1044 	bzero( kf, sizeof( CachedFont));
1045 	kf-> id = s-> fid;
1046 	kf-> fs = s;
1047 	memcpy( &kf-> font, &f-> font, sizeof( Font));
1048 	kf-> font. style &= ~(fsUnderlined|fsOutline|fsStruckOut) & fsMask;
1049 	kf-> font. pitch &= fpMask;
1050 	kf-> flags = f-> flags;
1051 	kf-> underlinePos = uPos;
1052 	kf-> underlineThickness = uThinkness;
1053 	kf-> refCnt = 0;
1054 	hash_store( guts. font_hash, key, sizeof( FontKey), kf);
1055 	return true;
1056 }
1057 
1058 void
prima_init_try_height(HeightGuessStack * p,int target,int firstMove)1059 prima_init_try_height( HeightGuessStack * p, int target, int firstMove )
1060 {
1061 	p-> locked = 0;
1062 	p-> sp     = 1;
1063 	p-> target = target;
1064 	p-> xlfd[0] = firstMove;
1065 }
1066 
1067 int
prima_try_height(HeightGuessStack * p,int height)1068 prima_try_height( HeightGuessStack * p, int height)
1069 {
1070 	int ret = -1;
1071 
1072 	if ( p-> locked != 0) return p-> locked;
1073 	if ( p-> sp >= MAX_HGS_SIZE) goto DONT_ADVISE;
1074 
1075 	if ( p-> sp > 1) {
1076 		int x0 = p-> xlfd[ p-> sp - 2];
1077 		int x1 = p-> xlfd[ p-> sp - 1];
1078 		int y0 = p-> prima[ p-> sp - 2];
1079 		int y1 = p-> prima[ p-> sp - 1] = height;
1080 		int d0 = y0 - p-> target;
1081 		int d1 = y1 - p-> target;
1082 		if (( d0 < 0 && d1 < 0 && d1 >= d0) || ( d0 > 0 && d1 > 0 && d1 <= d0)) { /* underflow */
1083 			int sp = p-> sp - 2;
1084 			while ( d1 == d0 && sp-- > 0) { /* not even moved */
1085 				x0 = p-> xlfd[ sp];
1086 				y0 = p-> prima[ sp];
1087 				d0 = y0 - p-> target;
1088 			}
1089 			if ( d1 != d0) {
1090 				ret = x0 + ( x1 - x0) * ( p-> target - y0) / ( y1 - y0) + 0.5;
1091 				if ( ret == x1 ) ret += ( d1 < 0) ? 1 : -1;
1092 				if ( ret < 1 ) ret = 1;
1093 			}
1094 		} else if ((( d0 < 0 && d1 > 0) || ( d0 > 0 && d1 < 0)) && ( abs(x0 - x1) > 1)) { /* overflow */
1095 			ret = x0 + ( x1 - x0) * ( p-> target - y0) / ( y1 - y0) + 0.5;
1096 			if ( ret == x0 ) ret += ( d0 < 0) ? 1 : -1; else
1097 			if ( ret == x1 ) ret += ( d1 < 0) ? 1 : -1;
1098 			if ( ret < 1 ) ret = 1;
1099 		}
1100 		/* printf("-- [%d=>%d],[%d=>%d] (%d %d)\n", x0, y0, x1, y1, d0, d1); */
1101 	} else {
1102 		if ( height > 0) /* sp == 0 */
1103 			ret = p-> xlfd[0] * p-> target / height;
1104 		p-> prima[ p-> sp - 1] = height;
1105 	}
1106 
1107 	if ( ret > 0) {
1108 		int i;
1109 		for ( i = 0; i < p-> sp; i++)
1110 			if ( p-> xlfd[ i] == ret) { /* advised already? */
1111 				ret = -1;
1112 				break;
1113 			}
1114 	}
1115 
1116 	p-> xlfd[ p-> sp] = ret;
1117 
1118 	p-> sp++;
1119 
1120 DONT_ADVISE:
1121 	if ( ret < 0) { /* select closest match */
1122 		int i, best_i = -1, best_diff = INT_MAX, diff, sp = p-> sp - 1;
1123 		for ( i = 0; i < sp; i++) {
1124 			diff = p-> target - p-> prima[i];
1125 			if ( diff < 0) diff += 1000;
1126 			if ( best_diff > diff) {
1127 				if ( p-> xlfd[i] < 0 ) continue;
1128 				best_diff = diff;
1129 				best_i = i;
1130 			}
1131 		}
1132 		if ( best_i >= 0 && p-> xlfd[best_i] != p-> xlfd[ sp - 1])
1133 			ret = p-> xlfd[best_i];
1134 		p-> locked = -1;
1135 	}
1136 
1137 	return ret;
1138 }
1139 
1140 static void
detail_font_info(PFontInfo f,PFont font,Bool addToCache,Bool bySize)1141 detail_font_info( PFontInfo f, PFont font, Bool addToCache, Bool bySize)
1142 {
1143 	XFontStruct *s = NULL;
1144 	unsigned long v;
1145 	char name[ 1024];
1146 	FontInfo fi;
1147 	PFontInfo of = f;
1148 	int height = 0, size = 0;
1149 	FontKey key;
1150 	Bool askedDefaultPitch;
1151 	HeightGuessStack hgs;
1152 
1153 	if ( f-> vecname) {
1154 		if ( bySize)
1155 			size = font-> size * 10;
1156 		else {
1157 			height = font-> height * 10;
1158 			prima_init_try_height( &hgs, height, f-> flags. heights_cache ? f-> heights_cache[0] : height);
1159 			if ( f-> flags. heights_cache)
1160 				height = prima_try_height( &hgs, f-> heights_cache[1]);
1161 		}
1162 	}
1163 
1164 AGAIN:
1165 	if ( f-> vecname) {
1166 		memmove( &fi, f, sizeof( fi));
1167 		fi. flags. size = fi. flags. height = fi. flags. width = fi. flags. ascent =
1168 			fi. flags. descent = fi. flags. internalLeading = fi. flags. externalLeading = 0;
1169 		f = &fi;
1170 
1171 		if ( f-> flags. bad_vector) {
1172 			/* three fields */
1173 			sprintf( name, f-> vecname, height / 10, size, font-> width * 10);
1174 		} else {
1175 			/* five fields */
1176 			sprintf( name, f-> vecname, height / 10, size, 0, 0, font-> width * 10);
1177 		}
1178 		Fdebug("font: construct h=%g, s=%d\n", (float)height/10, size);
1179 	} else {
1180 		strcpy( name, f-> xname);
1181 	}
1182 
1183 	Fdebug( "font: loading %s\n", name);
1184 	s = hash_fetch( xfontCache, name, strlen( name));
1185 
1186 	if ( !s) {
1187 		s = XLoadQueryFont( DISP, name);
1188 		XCHECKPOINT;
1189 		if ( !s) {
1190 			if ( !font)
1191 				warn( "font %s load error", name);
1192 			if ( of-> flags. disabled)
1193 				warn( "font %s pick-up error", name);
1194 			of-> flags. disabled = true;
1195 
1196 			Fdebug( "font: kill %s\n", name);
1197 			if ( font) prima_font_pp2font( "fixed", font);
1198 			of-> flags. disabled = false;
1199 			return;
1200 		} else {
1201 			hash_store( xfontCache, name, strlen( name), s);
1202 		}
1203 	}
1204 
1205 	if ( f-> flags. sloppy || f-> vecname) {
1206 		/* check first if height is o.k. */
1207 		int h = s-> max_bounds. ascent + s-> max_bounds. descent;
1208 		if ( h > 0 ) /* empty fonts like 'space' or just downright broken fonts */
1209 			f-> font. height = h;
1210 		f-> flags. height = true;
1211 		if ( f-> vecname && !bySize && f-> font. height != font-> height) {
1212 			int h = prima_try_height( &hgs, f-> font. height * 10);
1213 			Fdebug("font height pick: %d::%d => %d, advised %d\n", hgs.sp-1, font-> height, f-> font. height, h);
1214 			if ( h > 9) {
1215 				if ( !of-> flags. heights_cache) {
1216 					of-> heights_cache[0] = font-> height * 10;
1217 					of-> heights_cache[1] = f-> font. height;
1218 				}
1219 				height = h;
1220 				goto AGAIN;
1221 			}
1222 		}
1223 
1224 		/* detailing y-resolution */
1225 		if ( !f-> flags. yDeviceRes) {
1226 			if ( XGetFontProperty( s, FXA_RESOLUTION_Y, &v) && v) {
1227 				XCHECKPOINT;
1228 				f-> font. yDeviceRes = v;
1229 			} else {
1230 				f-> font. yDeviceRes = 72;
1231 			}
1232 			f-> flags. yDeviceRes = true;
1233 		}
1234 
1235 		/* detailing x-resolution */
1236 		if ( !f-> flags. xDeviceRes) {
1237 			if ( XGetFontProperty( s, FXA_RESOLUTION_X, &v) && v) {
1238 				XCHECKPOINT;
1239 				f-> font. xDeviceRes = v;
1240 			} else {
1241 				f-> font. xDeviceRes = 72;
1242 			}
1243 			f-> flags. xDeviceRes = true;
1244 		}
1245 
1246 		/* detailing internal leading */
1247 		if ( !f-> flags. internalLeading) {
1248 			if ( XGetFontProperty( s, FXA_CAP_HEIGHT, &v) && v) {
1249 				XCHECKPOINT;
1250 				f-> font. internalLeading = s-> max_bounds. ascent - v;
1251 			} else {
1252 				if ( f-> flags. height) {
1253 					f-> font. internalLeading = s-> max_bounds. ascent + s-> max_bounds. descent - f-> font. height;
1254 				} else if ( f-> flags. size) {
1255 					f-> font. internalLeading = s-> max_bounds. ascent + s-> max_bounds. descent -
1256 						( f-> font. size * f-> font. yDeviceRes) / 72.27 + 0.5;
1257 				} else {
1258 					f-> font. internalLeading = 0;
1259 				}
1260 			}
1261 			f-> flags. internalLeading = true;
1262 		}
1263 
1264 		/* detailing point size and height */
1265 		if ( bySize) {
1266 			if ( f-> vecname)
1267 				f-> font. size = size / 10;
1268 			else if ( !f-> flags. size)
1269 				f-> font. size = font-> size;
1270 		} else {
1271 
1272 			if ( f-> vecname && f-> font. height < font-> height) { /* adjust anyway */
1273 				f-> font. internalLeading += font-> height - f-> font. height;
1274 				f-> font. height = font-> height;
1275 			}
1276 
1277 			if ( !f-> flags. size) {
1278 				if ( XGetFontProperty( s, FXA_POINT_SIZE, &v) && v) {
1279 					XCHECKPOINT;
1280 					f-> font. size = ( v < 10) ? 1 : ( v / 10);
1281 				} else
1282 					f-> font. size = ( f-> font. height - f-> font. internalLeading) * 72.27 / f-> font. height + 0.5;
1283 			}
1284 		}
1285 		f-> flags. size = true;
1286 
1287 		/* misc stuff */
1288 		f-> flags. resolution      = true;
1289 		f-> font. resolution       = f-> font. yDeviceRes * 0x10000 + f-> font. xDeviceRes;
1290 		f-> flags. ascent          = true;
1291 		f-> font. ascent           = f-> font. height - s-> max_bounds. descent;
1292 		f-> flags. descent         = true;
1293 		f-> font. descent          = s-> max_bounds. descent;
1294 		f-> flags. defaultChar     = true;
1295 		f-> font. defaultChar      = s-> default_char;
1296 		f-> flags. firstChar       = true;
1297 		f-> font.  firstChar       = s-> min_byte1 * 256 + s-> min_char_or_byte2;
1298 		f-> flags. lastChar        = true;
1299 		f-> font.  lastChar        = s-> max_byte1 * 256 + s-> max_char_or_byte2;
1300 		f-> flags. direction       = true;
1301 		f-> font.  direction       = 0;
1302 		f-> flags. externalLeading = true;
1303 		f-> font.  externalLeading =
1304 						abs( s-> max_bounds. ascent  - s-> ascent) +
1305 						abs( s-> max_bounds. descent - s-> descent);
1306 		bzero(&f->font.is_utf8, sizeof(f->font.is_utf8));
1307 
1308 		/* detailing width */
1309 		if ( f-> font. width == 0 || !f-> flags. width) {
1310 			 if ( f-> vecname && font-> width > 0) {
1311 				f-> font. width = font-> width;
1312 				Fdebug("font: width = copy as is %d\n", f->font.width);
1313 			} else if ( XGetFontProperty( s, FXA_AVERAGE_WIDTH, &v) && v) {
1314 				XCHECKPOINT;
1315 				f-> font. width = (v + 5) / 10;
1316 				Fdebug("font: width = FXA_AVERAGE_WIDTH %d(%d)\n", f->font.width, v);
1317 			} else {
1318 				f-> font. width = s-> max_bounds. width;
1319 				Fdebug("font: width = max_width %d\n", f->font.width);
1320 			}
1321 		}
1322 		f-> flags. width = true;
1323 
1324 		/* detailing maximalWidth */
1325 		if ( !f-> flags. maximalWidth) {
1326 			f-> flags. maximalWidth = true;
1327 			if ( s-> per_char) {
1328 				int kl = 0, kr = 255, k;
1329 				int rl = 0, rr = 255, r, d;
1330 				f-> font. maximalWidth = 0;
1331 				if ( rl < s-> min_byte1) rl = s-> min_byte1;
1332 				if ( rr > s-> max_byte1) rr = s-> max_byte1;
1333 				if ( kl < s-> min_char_or_byte2) kl = s-> min_char_or_byte2;
1334 				if ( kr > s-> max_char_or_byte2) kr = s-> max_char_or_byte2;
1335 				d = kr - kl + 1;
1336 				for ( r = rl; r <= rr; r++)
1337 					for ( k = kl; k <= kr; k++) {
1338 						int x = s-> per_char[( r - s-> min_byte1) * d + k - s-> min_char_or_byte2]. width;
1339 						if ( f-> font. maximalWidth < x)
1340 							f-> font. maximalWidth = x;
1341 					}
1342 			} else {
1343 				f-> font. width = f-> font. maximalWidth = s-> max_bounds. width;
1344 				Fdebug("font: force width = max_width %d\n", f->font.width);
1345 			}
1346 		}
1347 		of-> flags. sloppy = false;
1348 	}
1349 
1350 	if ( addToCache && font) {
1351 		/* detailing stuff */
1352 		int underlinePos = 0, underlineThickness = 1;
1353 
1354 		/* trust the slant part of style */
1355 		font-> style = f-> font. style;
1356 
1357 		/* detailing underline things */
1358 		if ( XGetFontProperty( s, XA_UNDERLINE_POSITION, &v) && v) {
1359 			XCHECKPOINT;
1360 			underlinePos =  -s-> max_bounds. descent + v;
1361 		} else
1362 			underlinePos = - s-> max_bounds. descent + 1;
1363 
1364 		if ( XGetFontProperty( s, XA_UNDERLINE_THICKNESS, &v) && v) {
1365 			XCHECKPOINT;
1366 			underlineThickness = v;
1367 		} else
1368 			underlineThickness = 1;
1369 
1370 		underlinePos -= underlineThickness;
1371 		if ( -underlinePos + underlineThickness / 2 > s-> max_bounds. descent)
1372 			underlinePos = -s-> max_bounds. descent + underlineThickness / 2;
1373 
1374 		prima_build_font_key( &key, font, bySize);
1375 		Fdebug("font cache add: %d(%d)x%d.%s.%s %s/%s\n", f-> font.height, f-> font.size, f->font.width, _F_DEBUG_STYLE(f-> font. style), _F_DEBUG_PITCH(f->font. pitch), f-> font.name, f-> font.encoding);
1376 		if ( !add_font_to_cache( &key, f, name, s, underlinePos, underlineThickness))
1377 			return;
1378 		askedDefaultPitch = font-> pitch == fpDefault;
1379 		memcpy( font, &f-> font, sizeof( Font));
1380 		prima_build_font_key( &key, font, false);
1381 		if ( !hash_fetch( guts. font_hash, &key, sizeof( FontKey))) {
1382 			if ( !add_font_to_cache( &key, f, name, s, underlinePos, underlineThickness))
1383 				return;
1384 		}
1385 
1386 		if ( askedDefaultPitch && font-> pitch != fpDefault) {
1387 			int pitch = font-> pitch;
1388 			font-> pitch = fpDefault;
1389 			prima_build_font_key( &key, font, false);
1390 			if ( !hash_fetch( guts. font_hash, &key, sizeof( FontKey))) {
1391 				if ( !add_font_to_cache( &key, f, name, s, underlinePos, underlineThickness))
1392 					return;
1393 			}
1394 			font-> pitch = pitch;
1395 		}
1396 	}
1397 }
1398 
1399 #define QUERYDIFF_BY_SIZE        (-1)
1400 #define QUERYDIFF_BY_HEIGHT      (-2)
1401 
1402 static double
query_diff(PFontInfo fi,PFont f,char * lcname,int selector)1403 query_diff( PFontInfo fi, PFont f, char * lcname, int selector)
1404 {
1405 	double diff = 0.0;
1406 	int enc_match = 0, name_match = 0;
1407 
1408 	if ( fi-> flags. encoding && f-> encoding[0]) {
1409 		if ( strcmp( f-> encoding, fi-> font. encoding) != 0)
1410 			diff += 16000.0;
1411 		else
1412 			enc_match = 1;
1413 	}
1414 
1415 	if ( guts. locale[0] && !f-> encoding[0] && fi-> flags. encoding) {
1416 		if ( strcmp( fi-> font. encoding, guts. locale) != 0)
1417 			diff += 50;
1418 	}
1419 
1420 	if ( fi->  flags. pitch) {
1421 		if ( f-> pitch == fpDefault && fi-> font. pitch == fpFixed) {
1422 			diff += 1.0;
1423 		} else if ( f-> pitch == fpFixed && fi-> font. pitch == fpVariable) {
1424 			diff += 16000.0;
1425 		} else if ( f-> pitch == fpVariable && fi-> font. pitch == fpFixed) {
1426 			diff += 16000.0;
1427 		}
1428 	} else if ( f-> pitch != fpDefault) {
1429 		diff += 10000.0;  /* 2/3 of the worst case */
1430 	}
1431 
1432 	if ( fi-> flags. name && stricmp( lcname, fi-> font. name) == 0) {
1433 		diff += 0.0;
1434 		name_match = 1;
1435 	} else if ( fi-> flags. family && stricmp( lcname, fi-> font. family) == 0) {
1436 		diff += 1000.0;
1437 	} else if ( fi-> flags. family && strcasestr( fi-> font. family, lcname)) {
1438 		diff += 2000.0;
1439 	} else if ( !fi-> flags. family) {
1440 		diff += 8000.0;
1441 	} else if ( fi-> flags. name && strcasestr( fi-> font. name, lcname)) {
1442 		diff += 7000.0;
1443 	} else {
1444 		diff += 10000.0;
1445 		if ( fi-> flags. funky && !enc_match) diff += 10000.0;
1446 	}
1447 
1448 	if ( !name_match && !fi-> flags. known ) diff += 2.0;
1449 
1450 	if ( fi-> font. vector > fvBitmap) {
1451 		if ( fi-> flags. bad_vector) {
1452 			diff += 20.0;
1453 		}
1454 	} else {
1455 		int a, b;
1456 		switch ( selector) {
1457 		case QUERYDIFF_BY_SIZE:
1458 			a = fi-> font. size;
1459 			b = f-> size;
1460 			break;
1461 		case QUERYDIFF_BY_HEIGHT:
1462 			a = fi-> font. height;
1463 			b = f-> height;
1464 			break;
1465 		default:
1466 			a = fi-> font. height;
1467 			b = fi-> flags. sloppy ? selector : f-> height;
1468 			break;
1469 		}
1470 		if ( a > b) {
1471 			if ( have_vector_fonts) diff += 1000000.0;
1472 			diff += 600.0;
1473 			diff += 150.0 * (a - b);
1474 		} else if ( a < b) {
1475 			if ( have_vector_fonts) diff += 1000000.0;
1476 			diff += 150.0 * ( b - a);
1477 		}
1478 	}
1479 
1480 	if ( f-> width) {
1481 		if ( fi-> font. vector > fvBitmap ) {
1482 			if ( fi-> flags. bad_vector) {
1483 				diff += 20.0;
1484 			}
1485 		} else {
1486 			if ( fi-> font. width > f-> width) {
1487 				if ( have_vector_fonts) diff += 1000000.0;
1488 				diff += 200.0;
1489 				diff += 50.0 * (fi->  font. width - f-> width);
1490 			} else if ( fi-> font. width < f-> width) {
1491 				if ( have_vector_fonts) diff += 1000000.0;
1492 				diff += 50.0 * (f-> width - fi->  font. width);
1493 			}
1494 		}
1495 	}
1496 
1497 	if ( fi->  flags. xDeviceRes && fi-> flags. yDeviceRes) {
1498 		diff += 30.0 * (int)fabs( 0.5 +
1499 			(float)( 100.0 * guts. resolution. y / guts. resolution. x) -
1500 			(float)( 100.0 * fi->  font. yDeviceRes / fi->  font. xDeviceRes));
1501 	}
1502 
1503 	if ( fi->  flags. yDeviceRes) {
1504 		diff += 1.0 * abs( guts. resolution. y - fi->  font. yDeviceRes);
1505 	}
1506 	if ( fi->  flags. xDeviceRes) {
1507 		diff += 1.0 * abs( guts. resolution. x - fi->  font. xDeviceRes);
1508 	}
1509 
1510 	if ( fi-> flags. style && ( f-> style & ~(fsUnderlined|fsOutline|fsStruckOut))== fi->  font. style) {
1511 		diff += 0.0;
1512 	} else if ( !fi->  flags. style) {
1513 		diff += 2000.0;
1514 	} else {
1515 		if (( f-> style & fsThin) != ( fi-> font. style & fsThin))
1516 			diff += 1500.0;
1517 		if (( f-> style & fsBold) != ( fi-> font. style & fsBold))
1518 			diff += 3000.0;
1519 		if (( f-> style & fsItalic) != ( fi-> font. style & fsItalic))
1520 			diff += 3000.0;
1521 	}
1522 	return diff;
1523 }
1524 
1525 Bool
prima_core_font_pick(Handle self,PFont source,PFont dest)1526 prima_core_font_pick( Handle self, PFont source, PFont dest)
1527 {
1528 	PFontInfo info = guts. font_info;
1529 	int i, n = guts. n_fonts, index, lastIndex;
1530 	Bool by_size = Drawable_font_add( self, source, dest);
1531 	int query_type = by_size ? QUERYDIFF_BY_SIZE : QUERYDIFF_BY_HEIGHT;
1532 	double minDiff;
1533 	char lcname[ 256];
1534 	Bool underlined = dest-> style & fsUnderlined;
1535 	Bool struckout  = dest-> style & fsStruckOut;
1536 	int  direction  = dest-> direction;
1537 	HeightGuessStack hgs;
1538 
1539 	if ( n == 0) return false;
1540 
1541 	if ( strcmp( dest-> name, "Default") == 0)
1542 		strcpy( dest-> name, "helvetica");
1543 
1544 	if ( prima_find_known_font( dest, true, by_size)) {
1545 		if ( underlined) dest-> style |= fsUnderlined;
1546 		if ( struckout) dest-> style |= fsStruckOut;
1547 		dest-> direction = direction;
1548 		return true;
1549 	}
1550 
1551 	if ( by_size) {
1552 		Fdebug("font reqS:%d(h=%d)x%d.%s.%s %s/%s\n", dest-> size, dest-> height, dest->width, _F_DEBUG_STYLE(dest-> style), _F_DEBUG_PITCH(dest-> pitch), dest-> name, dest-> encoding);
1553 	} else {
1554 		Fdebug("font reqH:%d(s=%d)x%d.%s.%s %s/%s\n", dest-> height, dest-> size, dest->width, _F_DEBUG_STYLE(dest-> style), _F_DEBUG_PITCH(dest-> pitch), dest-> name, dest-> encoding);
1555 	}
1556 
1557 	if ( !hash_fetch( encodings, dest-> encoding, strlen( dest-> encoding)))
1558 		dest-> encoding[0] = 0;
1559 
1560 	if ( !by_size) prima_init_try_height( &hgs, dest-> height, dest-> height);
1561 
1562 	str_lwr( lcname, dest-> name);
1563 AGAIN:
1564 	index = lastIndex = -1;
1565 	minDiff = INT_MAX;
1566 	for ( i = 0; i < n; i++) {
1567 		double diff;
1568 		if ( info[i]. flags. disabled) continue;
1569 		diff = query_diff( info + i, dest, lcname, query_type);
1570 		if ( diff < minDiff) {
1571 			lastIndex = index;
1572 			index = i;
1573 			minDiff = diff;
1574 		}
1575 		if ( diff < 1) break;
1576 	}
1577 
1578 	i = index;
1579 
1580 	Fdebug("font: #%d (diff=%g): %s\n", i, minDiff, info[i].xname);
1581 	Fdebug("font: pick:%d(%d)x%d.%s.%s %s/%s %s.%s\n", info[i].font. height, info[i].font. size, info[i].font. width, _F_DEBUG_STYLE(info[i].font. style), _F_DEBUG_PITCH(info[i].font. pitch), info[i].font. name, info[i].font. encoding, info[i].flags.sloppy?"sloppy":"", info[i].vecname?"vector":"");
1582 
1583 	if ( !by_size && info[ i]. flags. sloppy && !info[ i]. vecname) {
1584 		detail_font_info( info + i, dest, false, by_size);
1585 		if ( minDiff < query_diff( info + i, dest, lcname, by_size)) {
1586 			int h = prima_try_height( &hgs, info[i]. font. height);
1587 			if ( h > 0) {
1588 				query_type = h;
1589 				goto AGAIN;
1590 			}
1591 		}
1592 	}
1593 
1594 	detail_font_info( info + index, dest, true, by_size);
1595 
1596 	if ( underlined) dest-> style |= fsUnderlined;
1597 	if ( struckout) dest-> style |= fsStruckOut;
1598 	dest-> direction = direction;
1599 	return true;
1600 }
1601 
1602 Bool
prima_core_font_encoding(char * encoding)1603 prima_core_font_encoding( char * encoding)
1604 {
1605 	return hash_fetch( encodings, encoding, strlen( encoding)) != NULL;
1606 }
1607 
1608 Bool
apc_font_pick(Handle self,PFont source,PFont dest)1609 apc_font_pick( Handle self, PFont source, PFont dest)
1610 {
1611 #ifdef USE_XFT
1612 	if ( guts. use_xft) {
1613 		if ( prima_xft_font_pick( self, source, dest, NULL, NULL))
1614 			return true;
1615 	}
1616 #endif
1617 	return prima_core_font_pick( self, source, dest);
1618 }
1619 
1620 
1621 static PFont
spec_fonts(int * retCount)1622 spec_fonts( int *retCount)
1623 {
1624 	int i, count = guts. n_fonts;
1625 	PFontInfo info = guts. font_info;
1626 	PFont fmtx = NULL;
1627 	List list;
1628 	PHash hash = NULL;
1629 
1630 	list_create( &list, 256, 256);
1631 
1632 	*retCount = 0;
1633 
1634 
1635 	if ( !( hash = hash_create())) {
1636 		list_destroy( &list);
1637 		return NULL;
1638 	}
1639 
1640 	/* collect font info */
1641 	for ( i = 0; i < count; i++) {
1642 		int len;
1643 		PFont fm;
1644 		if ( info[ i]. flags. disabled) continue;
1645 
1646 		len = strlen( info[ i].font.name);
1647 
1648 		fm = hash_fetch( hash, info[ i].font.name, len);
1649 		if ( fm) {
1650 			char ** enc = (char**) fm-> encoding;
1651 			unsigned char * shift = (unsigned char*)enc + sizeof(char *) - 1;
1652 			if ( *shift + 2 < 256 / sizeof(char*)) {
1653 				int j, exists = 0;
1654 				for ( j = 1; j <= *shift; j++) {
1655 					if ( strcmp( enc[j], info[i].xname + info[i].info_offset) == 0) {
1656 						exists = 1;
1657 						break;
1658 					}
1659 				}
1660 				if ( exists) continue;
1661 				*(enc + ++(*shift)) = info[i].xname + info[i].info_offset;
1662 			}
1663 			continue;
1664 		}
1665 
1666 		if ( !( fm = ( PFont) malloc( sizeof( Font)))) {
1667 			if ( hash) hash_destroy( hash, false);
1668 			list_delete_all( &list, true);
1669 			list_destroy( &list);
1670 			return NULL;
1671 		}
1672 
1673 		*fm = info[i]. font;
1674 
1675 		{ /* multi-encoding format */
1676 			char ** enc = (char**) fm-> encoding;
1677 			unsigned char * shift = (unsigned char*)enc + sizeof(char *) - 1;
1678 			memset( fm-> encoding, 0, 256);
1679 			*(enc + ++(*shift)) = info[i].xname + info[i].info_offset;
1680 			hash_store( hash, info[ i].font.name, strlen( info[ i].font.name), fm);
1681 		}
1682 
1683 		list_add( &list, ( Handle) fm);
1684 	}
1685 
1686 	if ( hash) hash_destroy( hash, false);
1687 
1688 	if ( list. count == 0) goto Nothing;
1689 	fmtx = ( PFont) malloc( list. count * sizeof( Font));
1690 	if ( !fmtx) {
1691 		list_delete_all( &list, true);
1692 		list_destroy( &list);
1693 		return NULL;
1694 	}
1695 
1696 	*retCount = list. count;
1697 		for ( i = 0; i < list. count; i++)
1698 			memcpy( fmtx + i, ( void *) list. items[ i], sizeof( Font));
1699 	list_delete_all( &list, true);
1700 
1701 Nothing:
1702 	list_destroy( &list);
1703 
1704 #ifdef USE_XFT
1705 	if ( guts. use_xft)
1706 		fmtx = prima_xft_fonts( fmtx, NULL, NULL, retCount);
1707 #endif
1708 
1709 	return fmtx;
1710 }
1711 
1712 PFont
apc_fonts(Handle self,const char * facename,const char * encoding,int * retCount)1713 apc_fonts( Handle self, const char *facename, const char * encoding, int *retCount)
1714 {
1715 	int i, count = guts. n_fonts;
1716 	PFontInfo info = guts. font_info;
1717 	PFontInfo * table;
1718 	int n_table;
1719 	PFont fmtx;
1720 
1721 	if ( !facename && !encoding) return spec_fonts( retCount);
1722 
1723 	*retCount = 0;
1724 	n_table = 0;
1725 
1726 	/* stage 1 - browse through names and validate records */
1727 	table = malloc( count * sizeof( PFontInfo));
1728 	if ( !table && count > 0) return NULL;
1729 
1730 	if ( facename == NULL) {
1731 		PHash hash = hash_create();
1732 		for ( i = 0; i < count; i++) {
1733 			int len;
1734 			if ( info[ i]. flags. disabled) continue;
1735 			len = strlen( info[ i].font.name);
1736 			if ( hash_fetch( hash, info[ i].font.name, len) ||
1737 				strcmp( info[ i].xname + info[ i].info_offset, encoding) != 0)
1738 				continue;
1739 			hash_store( hash, info[ i].font.name, len, (void*)1);
1740 			table[ n_table++] = info + i;
1741 		}
1742 		hash_destroy( hash, false);
1743 		*retCount = n_table;
1744 	} else {
1745 		for ( i = 0; i < count; i++) {
1746 			if ( info[ i]. flags. disabled) continue;
1747 			if (
1748 				( stricmp( info[ i].font.name, facename) == 0) &&
1749 				(
1750 					!encoding ||
1751 					( strcmp( info[ i].xname + info[ i].info_offset, encoding) == 0)
1752 				)
1753 			) {
1754 				table[ n_table++] = info + i;
1755 			}
1756 		}
1757 		*retCount = n_table;
1758 	}
1759 
1760 	fmtx = malloc( n_table * sizeof( Font));
1761 	bzero( fmtx, n_table * sizeof( Font));
1762 	if ( !fmtx && n_table > 0) {
1763 		*retCount = 0;
1764 		free( table);
1765 		return NULL;
1766 	}
1767 	for ( i = 0; i < n_table; i++) {
1768 		fmtx[i] = table[i]-> font;
1769 	}
1770 	free( table);
1771 
1772 #ifdef USE_XFT
1773 	if ( guts. use_xft)
1774 		fmtx = prima_xft_fonts( fmtx, facename, encoding, retCount);
1775 #endif
1776 
1777 	return fmtx;
1778 }
1779 
1780 PHash
apc_font_encodings(Handle self)1781 apc_font_encodings( Handle self )
1782 {
1783 	HE *he;
1784 	PHash hash = hash_create();
1785 	if ( !hash) return NULL;
1786 
1787 #ifdef USE_XFT
1788 	if ( guts. use_xft)
1789 		prima_xft_font_encodings( hash);
1790 #endif
1791 
1792 	hv_iterinit(( HV*) encodings);
1793 	for (;;) {
1794 		if (( he = hv_iternext( encodings)) == NULL)
1795 			break;
1796 		hash_store( hash, HeKEY( he), HeKLEN( he), (void*)1);
1797 	}
1798 	return hash;
1799 }
1800 
1801 Bool
apc_gp_set_font(Handle self,PFont font)1802 apc_gp_set_font( Handle self, PFont font)
1803 {
1804 	DEFXX;
1805 	Bool reload;
1806 	PCachedFont kf;
1807 
1808 #ifdef USE_XFT
1809 	if ( guts. use_xft && prima_xft_set_font( self, font)) return true;
1810 #endif
1811 
1812 	kf = prima_find_known_font( font, false, false);
1813 	if ( !kf || !kf-> id) {
1814 		dump_font( font);
1815 		if ( DISP) warn( "internal error (kf:%p)", kf); /* the font was not cached, can't be */
1816 		return false;
1817 	}
1818 
1819 	reload = XX-> font != kf && XX-> font != NULL;
1820 
1821 	if ( reload) {
1822 		kf-> refCnt++;
1823 		if ( XX-> font && ( --XX-> font-> refCnt <= 0)) {
1824 			prima_free_rotated_entry( XX-> font);
1825 			XX-> font-> refCnt = 0;
1826 		}
1827 	}
1828 
1829 	XX-> font = kf;
1830 
1831 	if ( XX-> flags. paint) {
1832 		XX-> flags. reload_font = reload;
1833 		XSetFont( DISP, XX-> gc, XX-> font-> id);
1834 		XCHECKPOINT;
1835 	}
1836 
1837 	return true;
1838 }
1839 
1840 Bool
apc_menu_set_font(Handle self,PFont font)1841 apc_menu_set_font( Handle self, PFont font)
1842 {
1843 	DEFMM;
1844 	Bool xft_metrics = 0;
1845 	PCachedFont kf = NULL;
1846 
1847 	font-> direction = 0; /* skip unnecessary logic */
1848 
1849 #ifdef USE_XFT
1850 	if ( guts. use_xft) {
1851 		kf = prima_xft_get_cache( font);
1852 		if ( kf) xft_metrics = 1;
1853 	}
1854 #endif
1855 
1856 	if ( !kf) {
1857 		kf = prima_find_known_font( font, false, false);
1858 		if ( !kf || !kf-> id) {
1859 			dump_font( font);
1860 			warn( "internal error (kf:%p)", kf); /* the font was not cached, can't be */
1861 			return false;
1862 		}
1863 	}
1864 
1865 	XX-> font = kf;
1866 	if ( !xft_metrics) {
1867 		XX-> guillemots = XTextWidth( kf-> fs, ">>", 2);
1868 	} else {
1869 #ifdef USE_XFT
1870 		XX-> guillemots = prima_xft_get_text_width( kf, ">>", 2, toAddOverhangs, NULL, NULL);
1871 #endif
1872 	}
1873 	if ( !XX-> type. popup && X_WINDOW) {
1874 		if (( kf-> font. height + 4) != X(PComponent(self)-> owner)-> menuHeight) {
1875 			prima_window_reset_menu( PComponent(self)-> owner, kf-> font. height + MENU_ITEM_GAP * 2);
1876 			XResizeWindow( DISP, X_WINDOW, XX-> w-> sz.x, XX-> w-> sz.y = kf-> font. height + MENU_ITEM_GAP * 2);
1877 		} else if ( !XX-> paint_pending) {
1878 			XClearArea( DISP, X_WINDOW, 0, 0, XX-> w-> sz.x, XX-> w-> sz.y, true);
1879 			XX-> paint_pending = true;
1880 		}
1881 	}
1882 	return true;
1883 }
1884 
1885 int
apc_gp_get_glyph_outline(Handle self,int index,int flags,int ** buffer)1886 apc_gp_get_glyph_outline( Handle self, int index, int flags, int ** buffer)
1887 {
1888 #ifdef USE_XFT
1889 	if ( guts.use_xft && X(self)-> font-> xft)
1890 		return prima_xft_get_glyph_outline( self, index, flags, buffer);
1891 #endif
1892 	return -1;
1893 }
1894 
1895 
1896 Bool
prima_update_rotated_fonts(PCachedFont f,const char * text,int len,Bool wide,double direction,PRotatedFont * result,Bool * ok_to_not_rotate)1897 prima_update_rotated_fonts( PCachedFont f, const char * text, int len, Bool wide, double direction, PRotatedFont * result,
1898 	Bool * ok_to_not_rotate)
1899 {
1900 	PRotatedFont * pr = &f-> rotated;
1901 	PRotatedFont r = NULL;
1902 	int i;
1903 
1904 	while ( direction < 0)     direction += 360.0;
1905 	while ( direction > 360.0) direction -= 360.0;
1906 
1907 	/* granulate direction */
1908 	{
1909 		double g;
1910 		int x = f-> fs-> max_bounds. width;
1911 		int y = f-> fs-> max_bounds. ascent + f-> fs-> max_bounds. descent;
1912 		if ( x < y) x = y;
1913 		g = fabs(0.785398 - atan2(x+1,x)) * 90.0 / 3.14159265358;
1914 		if ( g > 0) direction = floor(direction / g) * g;
1915 	}
1916 
1917 	if ( direction == 0.0) {
1918 		if ( ok_to_not_rotate) *ok_to_not_rotate = true;
1919 		return false;
1920 	}
1921 	if ( ok_to_not_rotate) *ok_to_not_rotate = false;
1922 
1923 	/* finding record for given direction */
1924 	while (*pr) {
1925 		if ((*pr)-> direction == direction) {
1926 			r = *pr;
1927 			break;
1928 		}
1929 		pr = ( PRotatedFont *) &((*pr)-> next);
1930 	}
1931 
1932 	if ( !r) { /* creating startup values for new entry */
1933 		double sin1, cos1, sin2, cos2, rad;
1934 		int    rbox_x[4], rbox_y[4], box_x[4], box_y[4], box[4];
1935 		XGCValues xgv;
1936 
1937 		r = *pr = malloc( sizeof( RotatedFont));
1938 		if ( !r) {
1939 			warn("Not enough memory");
1940 			return false;
1941 		}
1942 		bzero( r, sizeof( RotatedFont));
1943 		r-> direction = direction;
1944 		r-> first1  = f-> fs-> min_byte1;
1945 		r-> first2  = f-> fs-> min_char_or_byte2;
1946 		r-> width   = ( f-> fs-> max_char_or_byte2 > 255 ? 255 : f-> fs-> max_char_or_byte2)
1947 			- r-> first2 + 1;
1948 		if ( r-> width < 0) r-> width = 0;
1949 		r-> height = f-> fs-> max_byte1 - f-> fs-> min_byte1 + 1;
1950 		r-> length = r-> width * r-> height;
1951 		r-> defaultChar1 = f-> fs-> default_char >> 8;
1952 		r-> defaultChar2 = f-> fs-> default_char & 0xff;
1953 
1954 		if ( r-> defaultChar1 < r-> first1 || r-> defaultChar1 >= r-> first1 + r-> height ||
1955 			r-> defaultChar2 < r-> first2 || r-> defaultChar2 >= r-> first2 + r-> width)
1956 			r-> defaultChar1 = r-> defaultChar2 = -1;
1957 
1958 		if ( r-> length > 0) {
1959 			if ( !( r-> map = malloc( r-> length * sizeof( void*)))) {
1960 				*pr = NULL;
1961 				free( r);
1962 				warn("Not enough memory");
1963 				return false;
1964 			}
1965 			bzero( r-> map, r-> length * sizeof( void*));
1966 		}
1967 		rad = direction * 3.14159265358 / 180.0;
1968 		r-> sin. l = ( sin1 = sin( -rad)) * UINT16_PRECISION;
1969 		r-> cos. l = ( cos1 = cos( -rad)) * UINT16_PRECISION;
1970 		r-> sin2.l = ( sin2 = sin(  rad)) * UINT16_PRECISION;
1971 		r-> cos2.l = ( cos2 = cos(  rad)) * UINT16_PRECISION;
1972 
1973 /*
1974 	1(0,y)  2(x,y)
1975 	0(0,0)  3(x,0)
1976 */
1977 		box_x[0] = box_y[0] = box_x[1] = box_y[3] = 0;
1978 		r-> orgBox. x = box_x[2] = box_x[3] = f-> fs-> max_bounds. width;
1979 		r-> orgBox. y = box_y[1] = box_y[2] = f-> fs-> max_bounds. ascent + f-> fs-> max_bounds. descent;
1980 		for ( i = 0; i < 4; i++) {
1981 			rbox_x[i] = box_x[i] * cos2 - box_y[i] * sin2 + 0.5;
1982 			rbox_y[i] = box_x[i] * sin2 + box_y[i] * cos2 + 0.5;
1983 			box[i] = 0;
1984 		}
1985 		for ( i = 0; i < 4; i++) {
1986 			if ( rbox_x[i] < box[0]) box[0] = rbox_x[i];
1987 			if ( rbox_y[i] < box[1]) box[1] = rbox_y[i];
1988 			if ( rbox_x[i] > box[2]) box[2] = rbox_x[i];
1989 			if ( rbox_y[i] > box[3]) box[3] = rbox_y[i];
1990 		}
1991 		r-> dimension. x = box[2] - box[0] + 1;
1992 		r-> dimension. y = box[3] - box[1] + 1;
1993 		r-> shift. x = box[0];
1994 		r-> shift. y = box[1];
1995 
1996 		r-> lineSize = (( r-> orgBox. x + 31) / 32) * 4;
1997 		if ( !( r-> arena_bits = malloc( r-> lineSize * r-> orgBox. y)))
1998 			goto FAILED;
1999 
2000 		r-> arena = XCreatePixmap( DISP, guts. root, r-> orgBox.x, r-> orgBox. y, 1);
2001 		if ( !r-> arena) {
2002 			free( r-> arena_bits);
2003 FAILED:
2004 			*pr = NULL;
2005 			free( r-> map);
2006 			free( r);
2007 			warn("Cannot create pixmap");
2008 			return false;
2009 		}
2010 		XCHECKPOINT;
2011 		r-> arena_gc = XCreateGC( DISP, r-> arena, 0, &xgv);
2012 		XCHECKPOINT;
2013 		XSetFont( DISP, r-> arena_gc, f-> id);
2014 		XCHECKPOINT;
2015 		XSetBackground( DISP, r-> arena_gc, 0);
2016 	}
2017 
2018 	/* processing character records */
2019 	for ( i = 0; i < len; i++) {
2020 		XChar2b index;
2021 		XCharStruct * cs;
2022 		XImage * ximage;
2023 		PrimaXImage * px;
2024 		Byte * ndata;
2025 
2026 		index. byte1 = wide ? (( XChar2b*) text + i)-> byte1 : 0;
2027 		index. byte2 = wide ? (( XChar2b*) text + i)-> byte2 : *((unsigned char*)text + i);
2028 
2029 		/* querying character */
2030 		if ( index. byte1 < r-> first1 || index. byte1 >= r-> first1 + r-> height ||
2031 			index. byte2 < r-> first2 || index. byte2 >= r-> first2 + r-> width) {
2032 			if ( r-> defaultChar1 < 0 || r-> defaultChar2 < 0) continue;
2033 			index. byte1 = ( unsigned char) r-> defaultChar1;
2034 			index. byte2 = ( unsigned char) r-> defaultChar2;
2035 		}
2036 
2037 		if ( r-> map[( index. byte1 - r-> first1) * r-> width + index. byte2 - r-> first2]) continue;
2038 		cs = f-> fs-> per_char ?
2039 			f-> fs-> per_char +
2040 				( index. byte1 - f-> fs-> min_byte1) * r-> width +
2041 				index. byte2 - f-> fs-> min_char_or_byte2 :
2042 			&(f-> fs-> min_bounds);
2043 		XSetForeground( DISP, r-> arena_gc, 0);
2044 		XFillRectangle( DISP, r-> arena, r-> arena_gc, 0, 0, r-> orgBox. x, r-> orgBox .y);
2045 		XSetForeground( DISP, r-> arena_gc, 1);
2046 		if (wide)
2047 			XDrawString16( DISP, r-> arena, r-> arena_gc,
2048 				( cs-> lbearing < 0) ? -cs-> lbearing : 0,
2049 				r-> orgBox. y - f-> fs-> max_bounds. descent,
2050 				&index, 1);
2051 		else
2052 			XDrawString( DISP, r-> arena, r-> arena_gc,
2053 				( cs-> lbearing < 0) ? -cs-> lbearing : 0,
2054 				r-> orgBox. y - f-> fs-> max_bounds. descent,
2055 				(const char *)&index. byte2, 1);
2056 		XCHECKPOINT;
2057 
2058 		/* getting glyph bits */
2059 		ximage = XGetImage( DISP, r-> arena, 0, 0, r-> orgBox. x, r-> orgBox. y, 1, XYPixmap);
2060 		if ( !ximage) {
2061 			warn("Can't get image %dx%d", r-> orgBox.x, r-> orgBox.y);
2062 			return false;
2063 		}
2064 		XCHECKPOINT;
2065 		prima_copy_xybitmap( r-> arena_bits, (Byte*)ximage-> data, r-> orgBox. x, r-> orgBox. y,
2066 			r-> lineSize,  ximage-> bytes_per_line);
2067 		XDestroyImage( ximage);
2068 
2069 		px = prima_prepare_ximage( r-> dimension. x, r-> dimension. y, CACHE_BITMAP);
2070 		if ( !px) return false;
2071 		ndata = ( Byte*) px-> data_alias;
2072 		bzero( ndata, px-> bytes_per_line_alias * r-> dimension. y);
2073 
2074 		/* rotating */
2075 		{
2076 			int x, y, fast = r-> orgBox. y * r-> orgBox. x > 600;
2077 			Fixed lx, ly;
2078 			Byte * dst = ndata + px-> bytes_per_line_alias * ( r-> dimension. y - 1);
2079 
2080 			for ( y = r-> shift. y; y < r-> shift. y + r-> dimension. y; y++) {
2081 				lx. l = r-> shift. x * r-> cos. l - y * r-> sin. l;
2082 				if ( fast)
2083 					lx. l += UINT16_PRECISION/2;
2084 				ly. l = r-> shift. x * r-> sin. l + y * r-> cos. l + UINT16_PRECISION/2;
2085 				if ( fast) {
2086 					for ( x = 0; x < r-> dimension. x; x++) {
2087 					if ( ly. i. i >= 0 && ly. i. i < r-> orgBox. y &&
2088 						lx. i. i >= 0 && lx. i. i < r-> orgBox. x) {
2089 							Byte * src = r-> arena_bits + r-> lineSize * ly. i. i;
2090 							if ( src[ lx . i. i >> 3] & ( 1 << ( 7 - ( lx . i. i & 7))))
2091 								dst[ x >> 3] |= 1 << ( 7 - ( x & 7));
2092 						}
2093 						lx. l += r-> cos. l;
2094 						ly. l += r-> sin. l;
2095 					}
2096 				} else {
2097 					for ( x = 0; x < r-> dimension. x; x++) {
2098 						if ( ly. i. i >= 0 && ly. i. i < r-> orgBox. y && lx. i. i >= 0 && lx. i. i < r-> orgBox. x) {
2099 							long pv;
2100 							Byte * src = r-> arena_bits + r-> lineSize * ly. i. i;
2101 							pv = 0;
2102 							if ( src[ lx . i. i >> 3] & ( 1 << ( 7 - ( lx . i. i & 7))))
2103 								pv += ( UINT16_PRECISION - lx. i. f);
2104 							if ( lx. i. i < r-> orgBox. x - 1) {
2105 								if ( src[( lx. i. i + 1) >> 3] & ( 1 << ( 7 - (( lx. i. i + 1) & 7))))
2106 									pv += lx. i. f;
2107 							} else {
2108 								if ( src[ lx . i. i >> 3] & ( 1 << ( 7 - ( lx . i. i & 7))))
2109 									pv += UINT16_PRECISION/2;
2110 							}
2111 							if ( pv >= UINT16_PRECISION/2)
2112 								dst[ x >> 3] |= 1 << ( 7 - ( x & 7));
2113 						}
2114 						lx. l += r-> cos. l;
2115 						ly. l += r-> sin. l;
2116 					}
2117 				}
2118 				dst -= px-> bytes_per_line_alias;
2119 			}
2120 		}
2121 
2122 		if ( guts. bit_order != MSBFirst)
2123 			prima_mirror_bytes( ndata, r-> dimension.y * px-> bytes_per_line_alias);
2124 		r-> map[( index. byte1 - r-> first1) * r-> width + index. byte2 - r-> first2] = px;
2125 	}
2126 
2127 	if ( result)
2128 		*result = r;
2129 
2130 	return true;
2131 }
2132 
2133 XCharStruct *
prima_char_struct(XFontStruct * xs,void * c,Bool wide)2134 prima_char_struct( XFontStruct * xs, void * c, Bool wide)
2135 {
2136 	XCharStruct * cs;
2137 	int d = xs-> max_char_or_byte2 - xs-> min_char_or_byte2 + 1;
2138 	int index1        = wide ? (( XChar2b*) c)-> byte1 : 0;
2139 	int index2        = wide ? (( XChar2b*) c)-> byte2 : *((char*)c);
2140 	int default_char1 = wide ? ( xs-> default_char >> 8) : 0;
2141 	int default_char2 = xs-> default_char & 0xff;
2142 
2143 	if ( default_char1 < xs-> min_byte1 ||
2144 		default_char1 > xs-> max_byte1)
2145 		default_char1 = xs-> min_byte1;
2146 	if ( default_char2 < xs-> min_char_or_byte2 ||
2147 		default_char2 > xs-> max_char_or_byte2)
2148 		default_char2 = xs-> min_char_or_byte2;
2149 
2150 	if ( index1 < xs-> min_byte1 ||
2151 		index1 > xs-> max_byte1) {
2152 		index1 = default_char1;
2153 		index2 = default_char2;
2154 	}
2155 	if ( index2 < xs-> min_char_or_byte2 ||
2156 		index2 > xs-> max_char_or_byte2) {
2157 		index1 = default_char1;
2158 		index2 = default_char2;
2159 	}
2160 	cs = xs-> per_char ?
2161 		xs-> per_char +
2162 		( index1 - xs-> min_byte1) * d +
2163 		( index2 - xs-> min_char_or_byte2) :
2164 		&(xs-> min_bounds);
2165 	return cs;
2166 }
2167 
2168 const char *
prima_font_debug_style(int style)2169 prima_font_debug_style(int style)
2170 {
2171 	static char buf[256];
2172 	char * p = buf;
2173 
2174 	if ( style & fsBold) *p++ = 'B';
2175 	if ( style & fsThin) *p++ = 'T';
2176 	if ( style & fsItalic) *p++ = 'I';
2177 	if ( style & fsUnderlined) *p++ = 'U';
2178 	if ( style & fsStruckOut) *p++ = 'S';
2179 	if ( style & fsOutline) *p++ = 'O';
2180 	if ( style & ~fsMask) *p++ = '+';
2181 	if ( style == 0) *p++ = '0';
2182 	*p++ = 0;
2183 
2184 	return buf;
2185 }
2186 
2187 unsigned long *
apc_gp_get_mapper_ranges(PFont font,int * count,unsigned int * flags)2188 apc_gp_get_mapper_ranges(PFont font, int * count, unsigned int * flags)
2189 {
2190 #ifdef USE_XFT
2191 	if ( do_xft )
2192 		return prima_xft_mapper_query_ranges(font, count, flags);
2193 #endif
2194 	*count = 0;
2195 	*flags = 0;
2196 	return NULL;
2197 }
2198 
2199