/
Fl_Xlib_Graphics_Driver_font_xft.cxx
1458 lines (1289 loc) · 50.7 KB
/
Fl_Xlib_Graphics_Driver_font_xft.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// More font utilities for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2023 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
#ifndef FL_DOXYGEN
#include "../../flstring.h"
#include "Fl_Xlib_Graphics_Driver.H"
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/fl_string_functions.h> // fl_strdup()
#include <FL/platform.H>
#include <FL/fl_utf8.h>
#include "Fl_Font.H"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <X11/Xft/Xft.h>
#include <X11/Xft/XftCompat.h>
Fl_XFont_On_Demand fl_xfont = 0;
#if ! USE_PANGO
// The predefined fonts that FLTK has with Xft but without Pango:
static Fl_Fontdesc built_in_table[] = {
#if 1
{" sans"},
{"Bsans"},
{"Isans"},
{"Psans"},
{" mono"},
{"Bmono"},
{"Imono"},
{"Pmono"},
{" serif"},
{"Bserif"},
{"Iserif"},
{"Pserif"},
{" symbol"},
{" screen"},
{"Bscreen"},
{" zapf dingbats"},
#else
{" helvetica"},
{"Bhelvetica"},
{"Ihelvetica"},
{"Phelvetica"},
{" courier"},
{"Bcourier"},
{"Icourier"},
{"Pcourier"},
{" times"},
{"Btimes"},
{"Itimes"},
{"Ptimes"},
{" symbol"},
{" lucidatypewriter"},
{"Blucidatypewriter"},
{" zapf dingbats"},
#endif
};
FL_EXPORT Fl_Fontdesc* fl_fonts = (Fl_Fontdesc*)built_in_table;
#endif // ! USE_PANGO
static void fl_xft_font(Fl_Xlib_Graphics_Driver *driver, Fl_Font fnum, Fl_Fontsize size, int angle);
// For some reason Xft produces errors if you destroy a window whose id
// still exists in an XftDraw structure. It would be nice if this is not
// true, a lot of junk is needed to try to stop this:
XftDraw* Fl_Xlib_Graphics_Driver::draw_ = 0;
Window Fl_Xlib_Graphics_Driver::draw_window = (Window)0;
Fl_Fontsize Fl_Xlib_Graphics_Driver::size_unscaled() {
return (Fl_Fontsize)(size_);
}
static void correct_extents (float s, int &dx, int &dy, int &w, int &h) {
if (int(s) == s) { // correct for extents non divisible by integral s
int delta = dx - int(dx/s)*s;
if (delta) {
dx -= delta; w += delta;
}
delta = -dy - int((-dy)/s)*s;
if (delta) {
dy -= delta; h += delta;
}
delta = h - int(h/s)*s;
if (delta) {
h += delta;
}
delta = w - int(w/s)*s;
if (delta) {
w += delta;
}
}
}
#if ! USE_PANGO
///////////////////////////////////////////////////////////
#define LOCAL_RAW_NAME_MAX 256
extern "C" {
// sort returned fontconfig font names
static int name_sort(const void *aa, const void *bb) {
// What should we do here? Just do a string compare for now...
// NOTE: This yeilds some oddities - in particular a Blah Bold font will be
// listed before Blah...
// Also - the fontconfig listing returns some faces that are effectively duplicates
// as far as fltk is concerned, e.g. where there are ko or ja variants that we
// can't distinguish (since we are not yet fully UTF-*) - should we strip them here?
return fl_ascii_strcasecmp(*(char**)aa, *(char**)bb);
} // end of name_sort
} // end of extern C section
// Read the "pretty" name we have derived from fontconfig then convert
// it into the format fltk uses internally for Xft names...
// This is just a mess - I should have tokenised the strings and gone from there,
// but I really thought this would be easier!
static void make_raw_name(char *raw, char *pretty)
{
// Input name will be "Some Name:style = Bold Italic" or whatever
// The plan is this:
// - the first char in the "raw" name becomes either I, B, P or " " for
// italic, bold, bold italic or normal - this seems to be the fltk way...
char *style = strchr(pretty, ':');
if (style)
{
*style = 0; // Terminate "name" string
style ++; // point to start of style section
}
// It is still possible that the "pretty" name has multiple comma separated entries
// I've seen this often in CJK fonts, for example... Keep only the first one... This
// is not ideal, the CJK fonts often have the name in utf8 in several languages. What
// we ought to do is use fontconfig to query the available languages and pick one... But which?
#if 0 // loop to keep the LAST name entry...
char *nm1 = pretty;
char *nm2 = strchr(nm1, ',');
while(nm2) {
nm1 = nm2 + 1;
nm2 = strchr(nm1, ',');
}
raw[0] = ' '; raw[1] = 0; // Default start of "raw name" text
strncat(raw, nm1, LOCAL_RAW_NAME_MAX-1); // only copy MAX-1 chars, we have already set cell 0
// Ensure raw is terminated, just in case the given name is infeasibly long...
raw[LOCAL_RAW_NAME_MAX-1] = 0;
#else // keep the first remaining name entry
char *nm2 = strchr(pretty, ',');
if(nm2) *nm2 = 0; // terminate name after first entry
raw[0] = ' '; raw[1] = 0; // Default start of "raw name" text
strncat(raw, pretty, LOCAL_RAW_NAME_MAX-1); // only copy MAX-1 chars, we have already set cell 0
// Ensure raw is terminated, just in case the given name is infeasibly long...
raw[LOCAL_RAW_NAME_MAX-1] = 0;
#endif
// At this point, the name is "marked" as regular...
if (style)
{
#define PLAIN 0
#define BOLD 1
#define ITALIC 2
#define BITALIC (BOLD | ITALIC)
int mods = PLAIN;
char *last = style + strlen(style) - 2;
// Now try and parse the style string - look for the "=" sign
style = strchr(style, '=');
while ((style) && (style < last))
{
int type;
while ((*style == '=') || (*style == ' ') || (*style == '\t') || (*style == ','))
{
style++; // Start of Style string
if ((style >= last) || (*style == 0)) continue;
}
type = toupper(style[0]);
switch (type)
{
// Things we might see: Regular Normal Bold Italic Oblique (??what??) Medium
// Roman Light Demi Sans SemiCondensed SuperBold Book... etc...
// Things we actually care about: Bold Italic Oblique SuperBold - Others???
case 'I':
if (strncasecmp(style, "Italic", 6) == 0)
{
mods |= ITALIC;
}
goto NEXT_STYLE;
case 'B':
if (strncasecmp(style, "Bold", 4) == 0)
{
if (!strstr(raw, " Demi Bold")) mods |= BOLD;
}
else if (strncasecmp(style, "Black", 5) == 0)
{
if (!strstr(raw, " Black")) strcat(raw, " Black");
}
goto NEXT_STYLE;
case 'D':
if (strncasecmp(style, "Demi Bold", 7) == 0)
{
if (!strstr(raw, " Demi Bold")) strcat(raw, " Demi Bold");
}
goto NEXT_STYLE;
case 'O':
if (strncasecmp(style, "Oblique", 7) == 0)
{
mods |= ITALIC;
}
goto NEXT_STYLE;
case 'S':
if (strncasecmp(style, "SuperBold", 9) == 0)
{
mods |= BOLD;
}
goto NEXT_STYLE;
case 'L':
if (strncasecmp(style, "Light", 5) == 0)
{
if (!strstr(raw, " Light")) strcat(raw, " Light");
}
goto NEXT_STYLE;
case 'M':
if (strncasecmp(style, "Medium", 6) == 0)
{
if (!strstr(raw, " Medium")) strcat(raw, " Medium");
}
goto NEXT_STYLE;
default: // find the next gap
goto NEXT_STYLE;
} // switch end
NEXT_STYLE:
while ((*style != ' ') && (*style != '\t') && (*style != ','))
{
style++;
if ((style >= last) || (*style == 0)) goto STYLE_DONE;
}
}
STYLE_DONE:
// Set the "modifier" character in the raw string
switch(mods)
{
case BOLD: raw[0] = 'B';
break;
case ITALIC: raw[0] = 'I';
break;
case BITALIC: raw[0] = 'P';
break;
default: raw[0] = ' ';
break;
}
}
} // make_raw_name
///////////////////////////////////////////////////////////
static int fl_free_font = FL_FREE_FONT;
// This function fills in the fltk font table with all the fonts that
// are found on the X server. It tries to place the fonts into families
// and to sort them so the first 4 in a family are normal, bold, italic,
// and bold italic.
// Uses the fontconfig lib to construct a list of all installed fonts.
// I tried using XftListFonts for this, but the API is tricky - and when
// I looked at the XftList* code, it calls the Fc* functions anyway, so...
//
// Also, for now I'm ignoring the "pattern_name" and just getting everything...
// AND I don't try and skip the fonts we've already loaded in the defaults.
// Blimey! What a hack!
Fl_Font Fl_Xlib_Graphics_Driver::set_fonts(const char* pattern_name)
{
FcFontSet *fnt_set; // Will hold the list of fonts we find
FcPattern *fnt_pattern; // Holds the generic "match all names" pattern
FcObjectSet *fnt_obj_set = 0; // Holds the generic "match all objects"
int j; // loop iterator variable
int font_count; // Total number of fonts found to process
char **full_list; // The list of font names we build
if (fl_free_font > FL_FREE_FONT) // already been here
return (Fl_Font)fl_free_font;
fl_open_display(); // Just in case...
// Make sure fontconfig is ready... is this necessary? The docs say it is
// safe to call it multiple times, so just go for it anyway!
if (!FcInit())
{
// What to do? Just return defaults...
return FL_FREE_FONT;
}
// Create a search pattern that will match every font name - I think this
// does the Right Thing, but am not certain...
//
// This could possibly be "enhanced" to pay attention to the requested
// "pattern_name"?
fnt_pattern = FcPatternCreate();
fnt_obj_set = FcObjectSetBuild(FC_FAMILY, FC_STYLE, (void *)0);
// Hopefully, this is a set of all the fonts...
fnt_set = FcFontList(0, fnt_pattern, fnt_obj_set);
// We don't need the fnt_pattern and fnt_obj_set any more, release them
FcPatternDestroy(fnt_pattern);
FcObjectSetDestroy(fnt_obj_set);
// Now, if we got any fonts, iterate through them...
if (fnt_set)
{
char *stop;
char *start;
char *first;
font_count = fnt_set->nfont; // How many fonts?
// Allocate array of char*'s to hold the name strings
full_list = (char **)malloc(sizeof(char *) * font_count);
// iterate through all the font patterns and get the names out...
for (j = 0; j < font_count; j++)
{
// NOTE: FcChar8 is a typedef of "unsigned char"...
FcChar8 *font; // String to hold the font's name
// Convert from fontconfig internal pattern to human readable name
// NOTE: This WILL malloc storage, so we need to free it later...
font = FcNameUnparse(fnt_set->fonts[j]);
// The returned strings look like this...
// Century Schoolbook:style=Bold Italic,fed kursiv,Fett Kursiv,...
// So the bit we want is up to the first comma - BUT some strings have
// more than one name, separated by, guess what?, a comma...
stop = start = first = 0;
stop = strchr((char *)font, ',');
start = strchr((char *)font, ':');
if ((stop) && (start) && (stop < start))
{
first = stop + 1; // discard first version of name
// find first comma *after* the end of the name
stop = strchr((char *)start, ',');
}
else
{
first = (char *)font; // name is just what was returned
}
// Truncate the name after the (english) modifiers description
// Matt: Actually, there is no guarantee that the *first* description is the English one.
// Matt: So we keep the entire description, just in case.
//if (stop)
//{
// *stop = 0; // Terminate the string at the first comma, if there is one
//}
// Copy the font description into our list
if (first == (char *)font)
{ // The listed name is still OK
full_list[j] = (char *)font;
}
else
{ // The listed name has been modified
full_list[j] = fl_strdup(first);
// Free the font name storage
free (font);
}
// replace "style=Regular" so strcmp sorts it first
if (start) {
char *reg = strstr(full_list[j], "=Regular");
if (reg) reg[1]='.';
}
}
// Release the fnt_set - we don't need it any more
FcFontSetDestroy(fnt_set);
// Sort the list into alphabetic order
qsort(full_list, font_count, sizeof(*full_list), name_sort);
// Now let us add the names we got to fltk's font list...
for (j = 0; j < font_count; j++)
{
if (full_list[j])
{
char xft_name[LOCAL_RAW_NAME_MAX];
char *stored_name;
// Parse the strings into FLTK-XFT style..
make_raw_name(xft_name, full_list[j]);
// NOTE: This just adds on AFTER the default fonts - no attempt is made
// to identify already loaded fonts. Is this bad?
stored_name = fl_strdup(xft_name);
Fl::set_font((Fl_Font)(j + FL_FREE_FONT), stored_name);
fl_free_font ++;
free(full_list[j]); // release that name from our internal array
}
}
// Now we are done with the list, release it fully
free(full_list);
}
return (Fl_Font)fl_free_font;
} // ::set_fonts
////////////////////////////////////////////////////////////////
//
// Draw fonts using Keith Packard's Xft library to provide anti-
// aliased text. Yow!
//
// Many thanks to Carl for making the original version of this.
//
// This font code only requires libXft to work. Contrary to popular
// belief there is no need to have FreeType, or the Xrender extension
// available to use this code. You will just get normal Xlib fonts
// (Xft calls them "core" fonts) The Xft algorithms for choosing
// these is about as good as the FLTK ones (I hope to fix it so it is
// exactly as good...), plus it can cache its results and share them
// between programs, so using this should be a win in all cases. Also
// it should be obvious by comparing this file and fl_font_x.cxx that
// it is a lot easier to program with Xft than with Xlib.
//
// Also, Xft supports UTF-8 text rendering directly, which will allow
// us to support UTF-8 on all platforms more easily.
//
// To actually get antialiasing you need the following:
//
// 1. You have XFree86 4
// 2. You have the XRender extension
// 3. Your X device driver supports the render extension
// 4. You have libXft
// 5. Your libXft has FreeType2 support compiled in
// 6. You have the FreeType2 library
//
// Distributions that have XFree86 4.0.3 or later should have all of this...
//
// Unlike some other Xft packages, I tried to keep this simple and not
// to work around the current problems in Xft by making the "patterns"
// complicated. I believe doing this defeats our ability to improve Xft
// itself. You should edit the ~/.xftconfig file to "fix" things, there
// are several web pages of information on how to do this.
//
//static const char* fl_encoding_ = "iso8859-1";
static const char* fl_encoding_ = "iso10646-1";
void Fl_Xlib_Graphics_Driver::font_unscaled(Fl_Font fnum, Fl_Fontsize size) {
fl_xft_font(this, fnum, size, 0);
}
static XftFont* fontopen(const char* name, /*Fl_Fontsize*/double size, bool core, int angle) {
// Check: does it look like we have been passed an old-school XLFD fontname?
bool is_xlfd = false;
int hyphen_count = 0;
int comma_count = 0;
unsigned len = strlen(name);
if (len > 512) len = 512; // ensure we are not passed an unbounded font name
for(unsigned idx = 0; idx < len; idx++) {
if(name[idx] == '-') hyphen_count++; // check for XLFD hyphens
if(name[idx] == ',') comma_count++; // are there multiple names?
}
if(hyphen_count >= 14) is_xlfd = true; // Not a robust check, but good enough?
fl_open_display();
if(!is_xlfd) { // Not an XLFD - open as a XFT style name
XftFont *the_font = NULL; // the font we will return;
XftPattern *fnt_pat = XftPatternCreate(); // the pattern we will use for matching
int slant = XFT_SLANT_ROMAN;
int weight = XFT_WEIGHT_MEDIUM;
/* This "converts" FLTK-style font names back into "regular" names, extracting
* the BOLD and ITALIC codes as it does so - all FLTK font names are prefixed
* by 'I' (italic) 'B' (bold) 'P' (bold italic) or ' ' (regular) modifiers.
* This gives a fairly limited font selection ability, but is retained for
* compatibility reasons. If you really need a more complex choice, you are best
* calling Fl::set_fonts(*) then selecting the font by font-index rather than by
* name anyway. Probably.
* If you want to load a font who's name does actually begin with I, B or P, you
* MUST use a leading space OR simply use lowercase for the name...
*/
/* This may be efficient, but it is non-obvious. */
switch (*name++) {
case 'I': slant = XFT_SLANT_ITALIC; break; // italic
case 'P': slant = XFT_SLANT_ITALIC; // bold-italic (falls-through)
case 'B': weight = XFT_WEIGHT_BOLD; break; // bold
case ' ': break; // regular
default: name--; // no prefix, restore name
}
if(comma_count) { // multiple comma-separated names were passed
char *local_name = fl_strdup(name); // duplicate the full name so we can edit the copy
char *curr = local_name; // points to first name in string
char *nxt; // next name in string
do {
nxt = strchr(curr, ','); // find comma separator
if (nxt) {
*nxt = 0; // terminate first name
nxt++; // first char of next name
}
// Add the current name to the match pattern
XftPatternAddString(fnt_pat, XFT_FAMILY, curr);
if(nxt) curr = nxt; // move onto next name (if it exists)
// Now do a cut-down version of the FLTK name conversion.
// NOTE: we only use the slant and weight of the first name,
// subsequent names we ignore this for... But we still need to do the check.
switch (*curr++) {
case 'I': break; // italic
case 'P': // bold-italic (falls-through)
case 'B': break; // bold
case ' ': break; // regular
default: curr--; // no prefix, restore name
}
comma_count--; // decrement name sections count
} while (comma_count >= 0);
free(local_name); // release our local copy of font names
}
else { // single name was passed - add it directly
XftPatternAddString(fnt_pat, XFT_FAMILY, name);
}
// Construct a match pattern for the font we want...
XftPatternAddInteger(fnt_pat, XFT_WEIGHT, weight);
XftPatternAddInteger(fnt_pat, XFT_SLANT, slant);
XftPatternAddDouble (fnt_pat, XFT_PIXEL_SIZE, (double)size);
XftPatternAddString (fnt_pat, XFT_ENCODING, fl_encoding_);
// rotate font if angle!=0
if (angle !=0) {
XftMatrix m;
XftMatrixInit(&m);
XftMatrixRotate(&m,cos(M_PI*angle/180.),sin(M_PI*angle/180.));
XftPatternAddMatrix (fnt_pat, XFT_MATRIX,&m);
}
if (core) {
XftPatternAddBool(fnt_pat, XFT_CORE, FcTrue);
XftPatternAddBool(fnt_pat, XFT_RENDER, FcFalse);
}
XftPattern *match_pat; // the best available match on the system
XftResult match_result; // the result of our matching attempt
// query the system to find a match for this font
match_pat = XftFontMatch(fl_display, fl_screen, fnt_pat, &match_result);
#if 0 // the XftResult never seems to get set to anything... abandon this code?
switch(match_result) { // how good a match is this font for our request?
case XftResultMatch:
puts("Object exists with the specified ID");
break;
case XftResultTypeMismatch:
puts("Object exists, but the type does not match");
break;
case XftResultNoId:
puts("Object exists, but has fewer values than specified");
break;
case FcResultOutOfMemory:
puts("FcResult: Malloc failed");
break;
case XftResultNoMatch:
puts("Object does not exist at all");
break;
default:
printf("Invalid XftResult status %d \n", match_result);
break;
}
#endif
#if 0 // diagnostic to print the "full name" of the font we matched. This works.
FcChar8 *picked_name = FcNameUnparse(match_pat);
printf("Match: %s\n", picked_name);
free(picked_name);
#endif
// open the matched font
if (match_pat) the_font = XftFontOpenPattern(fl_display, match_pat);
if (!match_pat || !the_font) {
// last chance, just open any font in the right size
the_font = XftFontOpen (fl_display, fl_screen,
XFT_FAMILY, XftTypeString, "sans",
XFT_SIZE, XftTypeDouble, (double)size,
NULL);
XftPatternDestroy(fnt_pat);
if (!the_font) {
Fl::error("Unable to find fonts. Check your FontConfig configuration.\n");
exit(1);
}
return the_font;
}
#if 0 // diagnostic to print the "full name" of the font we actually opened. This works.
FcChar8 *picked_name2 = FcNameUnparse(the_font->pattern);
printf("Open : %s\n", picked_name2);
free(picked_name2);
#endif
XftPatternDestroy(fnt_pat);
// XftPatternDestroy(match_pat); // FontConfig will destroy this resource for us. We must not!
return the_font;
}
else { // We were passed a font name in XLFD format
/* OksiD's X font code could handle being passed a comma separated list
* of XLFD's. It then attempted to find which font was "best" from this list.
* But XftFontOpenXlfd can not do this, so if a list is passed, we just
* terminate it at the first comma.
* A "better" solution might be to use XftXlfdParse() on each of the passed
* XLFD's to construct a "super-pattern" that incorporates attributes from all
* XLFD's and use that to perform a XftFontMatch(). Maybe...
*/
char *local_name = fl_strdup(name);
if(comma_count) { // This means we were passed multiple XLFD's
char *pc = strchr(local_name, ',');
*pc = 0; // terminate the XLFD at the first comma
}
XftFont *the_font = XftFontOpenXlfd(fl_display, fl_screen, local_name);
free(local_name);
#if 0 // diagnostic to print the "full name" of the font we actually opened. This works.
puts("Font Opened"); fflush(stdout);
FcChar8 *picked_name2 = FcNameUnparse(the_font->pattern);
printf("Open : %s\n", picked_name2); fflush(stdout);
free(picked_name2);
#endif
return the_font;
}
} // end of fontopen
Fl_Xlib_Font_Descriptor::Fl_Xlib_Font_Descriptor(const char* name, Fl_Fontsize fsize, int fangle) : Fl_Font_Descriptor(name, fsize) {
// encoding = fl_encoding_;
angle = fangle;
font = fontopen(name, fsize, false, angle);
}
/* decodes the input UTF-8 string into a series of wchar_t characters.
n is set upon return to the number of characters.
Don't deallocate the returned memory.
*/
static const wchar_t *utf8reformat(const char *str, int& n)
{
static const wchar_t empty[] = {0};
static wchar_t *buffer;
static int lbuf = 0;
int newn;
if (n == 0) return empty;
newn = fl_utf8towc(str, n, (wchar_t*)buffer, lbuf);
if (newn >= lbuf) {
lbuf = newn + 100;
if (buffer) free(buffer);
buffer = (wchar_t*)malloc(lbuf * sizeof(wchar_t));
n = fl_utf8towc(str, n, (wchar_t*)buffer, lbuf);
} else {
n = newn;
}
return buffer;
}
static void utf8extents(Fl_Xlib_Font_Descriptor *desc, const char *str, int n, XGlyphInfo *extents)
{
memset(extents, 0, sizeof(XGlyphInfo));
const wchar_t *buffer = utf8reformat(str, n);
#ifdef __CYGWIN__
XftTextExtents16(fl_display, desc->font, (XftChar16 *)buffer, n, extents);
#else
XftTextExtents32(fl_display, desc->font, (XftChar32 *)buffer, n, extents);
#endif
}
int Fl_Xlib_Graphics_Driver::height_unscaled() {
if (font_descriptor()) return ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font->ascent + ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font->descent;
else return -1;
}
int Fl_Xlib_Graphics_Driver::descent_unscaled() {
if (font_descriptor()) return ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font->descent;
else return -1;
}
double Fl_Xlib_Graphics_Driver::width_unscaled(const char* str, int n) {
if (!font_descriptor()) return -1.0;
XGlyphInfo i;
utf8extents((Fl_Xlib_Font_Descriptor*)font_descriptor(), str, n, &i);
return i.xOff;
}
static double fl_xft_width(Fl_Font_Descriptor *desc, FcChar32 *str, int n) {
if (!desc) return -1.0;
XGlyphInfo i;
XftTextExtents32(fl_display, ((Fl_Xlib_Font_Descriptor*)desc)->font, str, n, &i);
return i.xOff;
}
double Fl_Xlib_Graphics_Driver::width_unscaled(unsigned int c) {
if (!font_descriptor()) return -1.0;
return fl_xft_width(font_descriptor(), (FcChar32 *)(&c), 1);
}
void Fl_Xlib_Graphics_Driver::text_extents_unscaled(const char *c, int n, int &dx, int &dy, int &w, int &h) {
if (!font_descriptor()) {
w = h = 0;
dx = dy = 0;
return;
}
XGlyphInfo gi;
utf8extents((Fl_Xlib_Font_Descriptor*)font_descriptor(), c, n, &gi);
w = gi.width;
h = gi.height;
dx = -gi.x ;
dy = -gi.y ;
correct_extents(scale(), dx, dy, w, h);
}
void Fl_Xlib_Graphics_Driver::draw_unscaled(const char *str, int n, int x, int y) {
// transform coordinates and clip if outside 16-bit space (STR 2798)
int x1 = x + floor(offset_x_) ;
if (x1 < clip_min() || x1 > clip_max()) return;
int y1 = y + floor(offset_y_) ;
if (y1 < clip_min() || y1 > clip_max()) return;
if (!draw_)
draw_ = XftDrawCreate(fl_display, draw_window = fl_window,
fl_visual->visual, fl_colormap);
else //if (draw_window != fl_window)
XftDrawChange(draw_, draw_window = fl_window);
Region region = (Region)fl_clip_region();
if (!(region && XEmptyRegion(region))) {
XftDrawSetClip(draw_, region);
// Use fltk's color allocator, copy the results to match what
// XftCollorAllocValue returns:
XftColor color;
color.pixel = fl_xpixel(Fl_Graphics_Driver::color());
uchar r,g,b; Fl::get_color(Fl_Graphics_Driver::color(), r,g,b);
color.color.red = ((int)r)*0x101;
color.color.green = ((int)g)*0x101;
color.color.blue = ((int)b)*0x101;
color.color.alpha = 0xffff;
const wchar_t *buffer = utf8reformat(str, n);
#ifdef __CYGWIN__
XftDrawString16(draw_, &color, ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font, x1, y1, (XftChar16 *)buffer, n);
#else
XftDrawString32(draw_, &color, ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font, x1, y1, (XftChar32 *)buffer, n);
#endif
}
}
void Fl_Xlib_Graphics_Driver::draw_unscaled(int angle, const char *str, int n, int x, int y) {
fl_xft_font(this, this->Fl_Graphics_Driver::font(), this->size_unscaled(), angle);
this->draw_unscaled(str, n, x, y);
fl_xft_font(this, this->Fl_Graphics_Driver::font(), this->size_unscaled(), 0);
}
void Fl_Xlib_Graphics_Driver::drawUCS4(const void *str, int n, int x, int y) {
if (!draw_)
draw_ = XftDrawCreate(fl_display, draw_window = fl_window,
fl_visual->visual, fl_colormap);
else //if (draw_window != fl_window)
XftDrawChange(draw_, draw_window = fl_window);
Region region = (Region)fl_clip_region();
if (region && XEmptyRegion(region)) return;
XftDrawSetClip(draw_, region);
// Use fltk's color allocator, copy the results to match what
// XftCollorAllocValue returns:
XftColor color;
color.pixel = fl_xpixel(this->color());
uchar r,g,b; Fl::get_color(this->color(), r,g,b);
color.color.red = ((int)r)*0x101;
color.color.green = ((int)g)*0x101;
color.color.blue = ((int)b)*0x101;
color.color.alpha = 0xffff;
XftDrawString32(draw_, &color, ((Fl_Xlib_Font_Descriptor*)font_descriptor())->font, x+floor(offset_x_), y+floor(offset_y_), (FcChar32 *)str, n);
}
void Fl_Xlib_Graphics_Driver::rtl_draw_unscaled(const char* c, int n, int x, int y) {
// clip if outside 16-bit space (STR 2798)
if (x < clip_min() || x > clip_max()) return;
if (y < clip_min() || y > clip_max()) return;
#if defined(__GNUC__)
// FIXME: warning Need to improve this XFT right to left draw function
#endif /*__GNUC__*/
// This actually draws LtoR, but aligned to R edge with the glyph order reversed...
// but you can't just byte-rev a UTF-8 string, that isn't valid.
// You can reverse a UCS4 string though...
int num_chars, wid, utf_len = strlen(c);
FcChar8 *u8 = (FcChar8 *)c;
FcBool valid = FcUtf8Len(u8, utf_len, &num_chars, &wid);
if (!valid)
{
// badly formed Utf-8 input string
return;
}
if (num_chars < n) n = num_chars; // limit drawing to usable characters in input array
FcChar32 *ucs_txt = new FcChar32[n+1];
FcChar32* pu;
int out, sz;
ucs_txt[n] = 0;
out = n-1;
while ((out >= 0) && (utf_len > 0))
{
pu = &ucs_txt[out];
sz = FcUtf8ToUcs4(u8, pu, utf_len);
utf_len = utf_len - sz;
u8 = u8 + sz;
out = out - 1;
}
// Now we have a UCS4 version of the input text, reversed, in ucs_txt
int offs = (int)fl_xft_width(font_descriptor(), ucs_txt, n);
drawUCS4(ucs_txt, n, (x-offs), y);
delete[] ucs_txt;
}
extern "C" {
static int int_sort(const void *aa, const void *bb) {
return (*(int*)aa)-(*(int*)bb);
}
}
////////////////////////////////////////////////////////////////
// Return all the point sizes supported by this font:
// Suprisingly enough Xft works exactly like fltk does and returns
// the same list. Except there is no way to tell if the font is scalable.
int Fl_Xlib_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) {
Fl_Fontdesc *s = fl_fonts+fnum;
if (!s->name) s = fl_fonts; // empty slot in table, use entry 0
fl_open_display();
XftFontSet* fs = XftListFonts(fl_display, fl_screen,
XFT_FAMILY, XftTypeString, s->name+1,
(void *)0,
XFT_PIXEL_SIZE,
(void *)0);
static int* array = 0;
static int array_size = 0;
if (fs->nfont >= array_size) {
delete[] array;
array = new int[array_size = fs->nfont+1];
}
array[0] = 0; int j = 1; // claim all fonts are scalable
for (int i = 0; i < fs->nfont; i++) {
double v;
if (XftPatternGetDouble(fs->fonts[i], XFT_PIXEL_SIZE, 0, &v) == XftResultMatch) {
array[j++] = int(v);
}
}
qsort(array+1, j-1, sizeof(int), int_sort);
XftFontSetDestroy(fs);
sizep = array;
return j;
}
#endif // !USE_PANGO
float Fl_Xlib_Graphics_Driver::scale_font_for_PostScript(Fl_Font_Descriptor *desc, int s) {
// Xft font height is sometimes larger than the required size (see STR 2566).
// Increase the PostScript font size by 15% without exceeding the display font height
int max = height_unscaled();
float ps_size = s * 1.15;
if (ps_size > max) ps_size = max;
return ps_size;
}
// Bug: older versions calculated the value for *ap as a side effect of
// making the name, and then forgot about it. To avoid having to change
// the header files I decided to store this value in the last character
// of the font name array.
#define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1
// turn a stored font name in "fltk format" into a pretty name:
const char* Fl_Xlib_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) {
Fl_Fontdesc *f = fl_fonts + fnum;
if (!f->fontname[0]) {
const char* p = f->name;
int type;
#if USE_PANGO
type = 0;
if (strstr(p, " Bold")) type = FL_BOLD;
if (strstr(p, " Italic") || strstr(p, " Oblique")) type += FL_ITALIC;
strlcpy(f->fontname, p, ENDOFBUFFER);
#else
switch (p[0]) {
case 'B': type = FL_BOLD; break;
case 'I': type = FL_ITALIC; break;
case 'P': type = FL_BOLD | FL_ITALIC; break;
default: type = 0; break;
}
// NOTE: This can cause duplications in fonts that already have Bold or Italic in
// their "name". Maybe we need to find a cleverer way?
strlcpy(f->fontname, p+1, ENDOFBUFFER);
if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER);
if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER);
#endif // USE_PANGO
f->fontname[ENDOFBUFFER] = (char)type;
}
if (ap) *ap = f->fontname[ENDOFBUFFER];
return f->fontname;
}
Fl_Xlib_Font_Descriptor::~Fl_Xlib_Font_Descriptor() {
if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL);
// XftFontClose(fl_display, font);
#if USE_PANGO
if (width) for (int i = 0; i < 64; i++) delete[] width[i];
delete[] width;
#endif
}
void Fl_Xlib_Graphics_Driver::destroy_xft_draw(Window id) {
if (id == draw_window)
XftDrawChange(draw_, draw_window = fl_message_window);
}
void *fl_xftfont = 0; // always 0 under Pango
static void fl_xft_font(Fl_Xlib_Graphics_Driver *driver, Fl_Font fnum, Fl_Fontsize size, int angle) {
if (fnum==-1) { // special case to stop font caching
driver->Fl_Graphics_Driver::font(0, 0);
return;
}
Fl_Xlib_Font_Descriptor* f = (Fl_Xlib_Font_Descriptor*)driver->font_descriptor();
if (fnum == driver->Fl_Graphics_Driver::font() && size == driver->size_unscaled() && f && f->angle == angle)
return;
driver->Fl_Graphics_Driver::font(fnum, size);
Fl_Fontdesc *font = fl_fonts + fnum;
// search the fontsizes we have generated already
for (f = (Fl_Xlib_Font_Descriptor*)font->first; f; f = (Fl_Xlib_Font_Descriptor*)f->next) {
if (f->size == size && f->angle == angle)// && !strcasecmp(f->encoding, fl_encoding_))
break;
}
if (!f) {
f = new Fl_Xlib_Font_Descriptor(font->name, size, angle);
f->next = font->first;
font->first = f;
}
driver->font_descriptor(f);
#if XFT_MAJOR < 2 && ! USE_PANGO
fl_xfont = f->font->u.core.font;
#else
fl_xfont = NULL; // invalidate
#endif // XFT_MAJOR < 2
#if USE_PANGO
fl_xftfont = NULL;
#else
fl_xftfont = (void*)f->font;