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