1/*
2 * tkUnixFont.c --
3 *
4 *	Contains the Unix implementation of the platform-independent font
5 *	package interface.
6 *
7 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id$
13 */
14
15#include "tkUnixInt.h"
16#include "tkFont.h"
17#include <netinet/in.h>		/* for htons() prototype */
18#include <arpa/inet.h>		/* inet_ntoa() */
19
20/*
21 * The preferred font encodings.
22 */
23
24static CONST char *encodingList[] = {
25    "iso8859-1", "jis0208", "jis0212", NULL
26};
27
28/*
29 * The following structure represents a font family. It is assumed that all
30 * screen fonts constructed from the same "font family" share certain
31 * properties; all screen fonts with the same "font family" point to a shared
32 * instance of this structure. The most important shared property is the
33 * character existence metrics, used to determine if a screen font can display
34 * a given Unicode character.
35 *
36 * Under Unix, there are three attributes that uniquely identify a "font
37 * family": the foundry, face name, and charset.
38 */
39
40#define FONTMAP_SHIFT		10
41
42#define FONTMAP_PAGES		(1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT))
43#define FONTMAP_BITSPERPAGE	(1 << FONTMAP_SHIFT)
44
45typedef struct FontFamily {
46    struct FontFamily *nextPtr;	/* Next in list of all known font families. */
47    int refCount;		/* How many SubFonts are referring to this
48				 * FontFamily. When the refCount drops to
49				 * zero, this FontFamily may be freed. */
50    /*
51     * Key.
52     */
53
54    Tk_Uid foundry;		/* Foundry key for this FontFamily. */
55    Tk_Uid faceName;		/* Face name key for this FontFamily. */
56    Tcl_Encoding encoding;	/* Encoding key for this FontFamily. */
57
58    /*
59     * Derived properties.
60     */
61
62    int isTwoByteFont;		/* 1 if this is a double-byte font, 0
63				 * otherwise. */
64    char *fontMap[FONTMAP_PAGES];
65				/* Two-level sparse table used to determine
66				 * quickly if the specified character exists.
67				 * As characters are encountered, more pages
68				 * in this table are dynamically alloced. The
69				 * contents of each page is a bitmask
70				 * consisting of FONTMAP_BITSPERPAGE bits,
71				 * representing whether this font can be used
72				 * to display the given character at the
73				 * corresponding bit position. The high bits
74				 * of the character are used to pick which
75				 * page of the table is used. */
76} FontFamily;
77
78/*
79 * The following structure encapsulates an individual screen font. A font
80 * object is made up of however many SubFonts are necessary to display a
81 * stream of multilingual characters.
82 */
83
84typedef struct SubFont {
85    char **fontMap;		/* Pointer to font map from the FontFamily,
86				 * cached here to save a dereference. */
87    XFontStruct *fontStructPtr;	/* The specific screen font that will be used
88				 * when displaying/measuring chars belonging
89				 * to the FontFamily. */
90    FontFamily *familyPtr;	/* The FontFamily for this SubFont. */
91} SubFont;
92
93/*
94 * The following structure represents Unix's implementation of a font object.
95 */
96
97#define SUBFONT_SPACE		3
98#define BASE_CHARS		256
99
100typedef struct UnixFont {
101    TkFont font;		/* Stuff used by generic font package. Must be
102				 * first in structure. */
103    SubFont staticSubFonts[SUBFONT_SPACE];
104				/* Builtin space for a limited number of
105				 * SubFonts. */
106    int numSubFonts;		/* Length of following array. */
107    SubFont *subFontArray;	/* Array of SubFonts that have been loaded in
108				 * order to draw/measure all the characters
109				 * encountered by this font so far. All fonts
110				 * start off with one SubFont initialized by
111				 * AllocFont() from the original set of font
112				 * attributes. Usually points to
113				 * staticSubFonts, but may point to malloced
114				 * space if there are lots of SubFonts. */
115    SubFont controlSubFont;	/* Font to use to display control-character
116				 * expansions. */
117
118    Display *display;		/* Display that owns font. */
119    int pixelSize;		/* Original pixel size used when font was
120				 * constructed. */
121    TkXLFDAttributes xa;	/* Additional attributes that specify the
122				 * preferred foundry and encoding to use when
123				 * constructing additional SubFonts. */
124    int widths[BASE_CHARS];	/* Widths of first 256 chars in the base font,
125				 * for handling common case. */
126    int underlinePos;		/* Offset from baseline to origin of underline
127				 * bar (used when drawing underlined font)
128				 * (pixels). */
129    int barHeight;		/* Height of underline or overstrike bar (used
130				 * when drawing underlined or strikeout font)
131				 * (pixels). */
132} UnixFont;
133
134/*
135 * The following structure and definition is used to keep track of the
136 * alternative names for various encodings. Asking for an encoding that
137 * matches one of the alias patterns will result in actually getting the
138 * encoding by its real name.
139 */
140
141typedef struct EncodingAlias {
142    char *realName;		/* The real name of the encoding to load if
143				 * the provided name matched the pattern. */
144    char *aliasPattern;		/* Pattern for encoding name, of the form that
145				 * is acceptable to Tcl_StringMatch. */
146} EncodingAlias;
147
148/*
149 * Just some utility structures used for passing around values in helper
150 * functions.
151 */
152
153typedef struct FontAttributes {
154    TkFontAttributes fa;
155    TkXLFDAttributes xa;
156} FontAttributes;
157
158typedef struct ThreadSpecificData {
159    FontFamily *fontFamilyList; /* The list of font families that are
160				 * currently loaded. As screen fonts are
161				 * loaded, this list grows to hold information
162				 * about what characters exist in each font
163				 * family. */
164    FontFamily controlFamily;	/* FontFamily used to handle control character
165				 * expansions. The encoding of this FontFamily
166				 * converts UTF-8 to backslashed escape
167				 * sequences. */
168} ThreadSpecificData;
169static Tcl_ThreadDataKey dataKey;
170
171/*
172 * The set of builtin encoding alises to convert the XLFD names for the
173 * encodings into the names expected by the Tcl encoding package.
174 */
175
176static EncodingAlias encodingAliases[] = {
177    {"gb2312-raw",	"gb2312*"},
178    {"big5",		"big5*"},
179    {"cns11643-1",	"cns11643*-1"},
180    {"cns11643-1",	"cns11643*.1-0"},
181    {"cns11643-2",	"cns11643*-2"},
182    {"cns11643-2",	"cns11643*.2-0"},
183    {"jis0201",		"jisx0201*"},
184    {"jis0201",		"jisx0202*"},
185    {"jis0208",		"jisc6226*"},
186    {"jis0208",		"jisx0208*"},
187    {"jis0212",		"jisx0212*"},
188    {"tis620",		"tis620*"},
189    {"ksc5601",		"ksc5601*"},
190    {"dingbats",	"*dingbats"},
191#ifdef WORDS_BIGENDIAN
192    {"unicode",		"iso10646-1"},
193#else
194    /*
195     * ucs-2be is needed if native order isn't BE.
196     */
197    {"ucs-2be",		"iso10646-1"},
198#endif
199    {NULL,		NULL}
200};
201
202/*
203 * Functions used only in this file.
204 */
205
206static void		FontPkgCleanup(ClientData clientData);
207static FontFamily *	AllocFontFamily(Display *display,
208			    XFontStruct *fontStructPtr, int base);
209static SubFont *	CanUseFallback(UnixFont *fontPtr,
210			    CONST char *fallbackName, int ch,
211			    SubFont **fixSubFontPtrPtr);
212static SubFont *	CanUseFallbackWithAliases(UnixFont *fontPtr,
213			    char *fallbackName, int ch,
214			    Tcl_DString *nameTriedPtr,
215			    SubFont **fixSubFontPtrPtr);
216static int		ControlUtfProc(ClientData clientData, CONST char *src,
217			    int srcLen, int flags, Tcl_EncodingState*statePtr,
218			    char *dst, int dstLen, int *srcReadPtr,
219			    int *dstWrotePtr, int *dstCharsPtr);
220static XFontStruct *	CreateClosestFont(Tk_Window tkwin,
221			    CONST TkFontAttributes *faPtr,
222			    CONST TkXLFDAttributes *xaPtr);
223static SubFont *	FindSubFontForChar(UnixFont *fontPtr, int ch,
224			    SubFont **fixSubFontPtrPtr);
225static void		FontMapInsert(SubFont *subFontPtr, int ch);
226static void		FontMapLoadPage(SubFont *subFontPtr, int row);
227static int		FontMapLookup(SubFont *subFontPtr, int ch);
228static void		FreeFontFamily(FontFamily *afPtr);
229static CONST char *	GetEncodingAlias(CONST char *name);
230static int		GetFontAttributes(Display *display,
231			    XFontStruct *fontStructPtr, FontAttributes *faPtr);
232static XFontStruct *	GetScreenFont(Display *display,
233			    FontAttributes *wantPtr, char **nameList,
234			    int bestIdx[], unsigned int bestScore[]);
235static XFontStruct *	GetSystemFont(Display *display);
236static int		IdentifySymbolEncodings(FontAttributes *faPtr);
237static void		InitFont(Tk_Window tkwin, XFontStruct *fontStructPtr,
238			    UnixFont *fontPtr);
239static void		InitSubFont(Display *display,
240			    XFontStruct *fontStructPtr, int base,
241			    SubFont *subFontPtr);
242static char **		ListFonts(Display *display, CONST char *faceName,
243			    int *numNamesPtr);
244static char **		ListFontOrAlias(Display *display, CONST char*faceName,
245			    int *numNamesPtr);
246static unsigned int	RankAttributes(FontAttributes *wantPtr,
247			    FontAttributes *gotPtr);
248static void		ReleaseFont(UnixFont *fontPtr);
249static void		ReleaseSubFont(Display *display, SubFont *subFontPtr);
250static int		SeenName(CONST char *name, Tcl_DString *dsPtr);
251#ifndef WORDS_BIGENDIAN
252static int		Ucs2beToUtfProc(ClientData clientData, CONST char*src,
253			    int srcLen, int flags, Tcl_EncodingState*statePtr,
254			    char *dst, int dstLen, int *srcReadPtr,
255			    int *dstWrotePtr, int *dstCharsPtr);
256static int		UtfToUcs2beProc(ClientData clientData, CONST char*src,
257			    int srcLen, int flags, Tcl_EncodingState*statePtr,
258			    char *dst, int dstLen, int *srcReadPtr,
259			    int *dstWrotePtr, int *dstCharsPtr);
260#endif
261
262/*
263 *-------------------------------------------------------------------------
264 *
265 * FontPkgCleanup --
266 *
267 *	This function is called when an application is created. It initializes
268 *	all the structures that are used by the platform-dependent code on a
269 *	per application basis.
270 *
271 * Results:
272 *	None.
273 *
274 * Side effects:
275 *	Releases thread-specific resources used by font pkg.
276 *
277 *-------------------------------------------------------------------------
278 */
279
280static void
281FontPkgCleanup(
282    ClientData clientData)
283{
284    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
285	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
286
287    if (tsdPtr->controlFamily.encoding != NULL) {
288	FontFamily *familyPtr = &tsdPtr->controlFamily;
289	int i;
290
291	Tcl_FreeEncoding(familyPtr->encoding);
292	for (i = 0; i < FONTMAP_PAGES; i++) {
293	    if (familyPtr->fontMap[i] != NULL) {
294		ckfree(familyPtr->fontMap[i]);
295	    }
296	}
297	tsdPtr->controlFamily.encoding = NULL;
298    }
299}
300
301/*
302 *-------------------------------------------------------------------------
303 *
304 * TkpFontPkgInit --
305 *
306 *	This function is called when an application is created. It initializes
307 *	all the structures that are used by the platform-dependent code on a
308 *	per application basis.
309 *
310 * Results:
311 *	None.
312 *
313 * Side effects:
314 *	None.
315 *
316 *-------------------------------------------------------------------------
317 */
318
319void
320TkpFontPkgInit(
321    TkMainInfo *mainPtr)	/* The application being created. */
322{
323    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
324	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
325    Tcl_EncodingType type;
326    SubFont dummy;
327    int i;
328
329    if (tsdPtr->controlFamily.encoding == NULL) {
330	type.encodingName = "X11ControlChars";
331	type.toUtfProc = ControlUtfProc;
332	type.fromUtfProc = ControlUtfProc;
333	type.freeProc = NULL;
334	type.clientData = NULL;
335	type.nullSize = 0;
336
337	tsdPtr->controlFamily.refCount = 2;
338	tsdPtr->controlFamily.encoding = Tcl_CreateEncoding(&type);
339	tsdPtr->controlFamily.isTwoByteFont = 0;
340
341	dummy.familyPtr = &tsdPtr->controlFamily;
342	dummy.fontMap = tsdPtr->controlFamily.fontMap;
343	for (i = 0x00; i < 0x20; i++) {
344	    FontMapInsert(&dummy, i);
345	    FontMapInsert(&dummy, i + 0x80);
346	}
347
348#ifndef WORDS_BIGENDIAN
349	/*
350	 * UCS-2BE is unicode (UCS-2) in big-endian format. Define this if
351	 * native order isn't BE. It is used in iso10646 fonts.
352	 */
353
354	type.encodingName = "ucs-2be";
355	type.toUtfProc = Ucs2beToUtfProc;
356	type.fromUtfProc = UtfToUcs2beProc;
357	type.freeProc = NULL;
358	type.clientData = NULL;
359	type.nullSize = 2;
360	Tcl_CreateEncoding(&type);
361#endif
362	Tcl_CreateThreadExitHandler(FontPkgCleanup, NULL);
363    }
364}
365
366/*
367 *-------------------------------------------------------------------------
368 *
369 * ControlUtfProc --
370 *
371 *	Convert from UTF-8 into the ASCII expansion of a control character.
372 *
373 * Results:
374 *	Returns TCL_OK if conversion was successful.
375 *
376 * Side effects:
377 *	None.
378 *
379 *-------------------------------------------------------------------------
380 */
381
382static int
383ControlUtfProc(
384    ClientData clientData,	/* Not used. */
385    CONST char *src,		/* Source string in UTF-8. */
386    int srcLen,			/* Source string length in bytes. */
387    int flags,			/* Conversion control flags. */
388    Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
389				 * information used during a piecewise
390				 * conversion. Contents of statePtr are
391				 * initialized and/or reset by conversion
392				 * routine under control of flags argument. */
393    char *dst,			/* Output buffer in which converted string is
394				 * stored. */
395    int dstLen,			/* The maximum length of output buffer in
396				 * bytes. */
397    int *srcReadPtr,		/* Filled with the number of bytes from the
398				 * source string that were converted. This may
399				 * be less than the original source length if
400				 * there was a problem converting some source
401				 * characters. */
402    int *dstWrotePtr,		/* Filled with the number of bytes that were
403				 * stored in the output buffer as a result of
404				 * the conversion. */
405    int *dstCharsPtr)		/* Filled with the number of characters that
406				 * correspond to the bytes stored in the
407				 * output buffer. */
408{
409    CONST char *srcStart, *srcEnd;
410    char *dstStart, *dstEnd;
411    Tcl_UniChar ch;
412    int result;
413    static char hexChars[] = "0123456789abcdef";
414    static char mapChars[] = {
415	0, 0, 0, 0, 0, 0, 0,
416	'a', 'b', 't', 'n', 'v', 'f', 'r'
417    };
418
419    result = TCL_OK;
420
421    srcStart = src;
422    srcEnd = src + srcLen;
423
424    dstStart = dst;
425    dstEnd = dst + dstLen - 6;
426
427    for ( ; src < srcEnd; ) {
428	if (dst > dstEnd) {
429	    result = TCL_CONVERT_NOSPACE;
430	    break;
431	}
432	src += Tcl_UtfToUniChar(src, &ch);
433	dst[0] = '\\';
434	if ((ch < sizeof(mapChars)) && (mapChars[ch] != 0)) {
435	    dst[1] = mapChars[ch];
436	    dst += 2;
437	} else if (ch < 256) {
438	    dst[1] = 'x';
439	    dst[2] = hexChars[(ch >> 4) & 0xf];
440	    dst[3] = hexChars[ch & 0xf];
441	    dst += 4;
442	} else {
443	    dst[1] = 'u';
444	    dst[2] = hexChars[(ch >> 12) & 0xf];
445	    dst[3] = hexChars[(ch >> 8) & 0xf];
446	    dst[4] = hexChars[(ch >> 4) & 0xf];
447	    dst[5] = hexChars[ch & 0xf];
448	    dst += 6;
449	}
450    }
451    *srcReadPtr = src - srcStart;
452    *dstWrotePtr = dst - dstStart;
453    *dstCharsPtr = dst - dstStart;
454    return result;
455}
456
457#ifndef WORDS_BIGENDIAN
458/*
459 *-------------------------------------------------------------------------
460 *
461 * Ucs2beToUtfProc --
462 *
463 *	Convert from UCS-2BE (big-endian 16-bit Unicode) to UTF-8.
464 *	This is only defined on LE machines.
465 *
466 * Results:
467 *	Returns TCL_OK if conversion was successful.
468 *
469 * Side effects:
470 *	None.
471 *
472 *-------------------------------------------------------------------------
473 */
474
475static int
476Ucs2beToUtfProc(
477    ClientData clientData,	/* Not used. */
478    CONST char *src,		/* Source string in Unicode. */
479    int srcLen,			/* Source string length in bytes. */
480    int flags,			/* Conversion control flags. */
481    Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
482				 * information used during a piecewise
483				 * conversion. Contents of statePtr are
484				 * initialized and/or reset by conversion
485				 * routine under control of flags argument. */
486    char *dst,			/* Output buffer in which converted string is
487				 * stored. */
488    int dstLen,			/* The maximum length of output buffer in
489				 * bytes. */
490    int *srcReadPtr,		/* Filled with the number of bytes from the
491				 * source string that were converted. This may
492				 * be less than the original source length if
493				 * there was a problem converting some source
494				 * characters. */
495    int *dstWrotePtr,		/* Filled with the number of bytes that were
496				 * stored in the output buffer as a result of
497				 * the conversion. */
498    int *dstCharsPtr)		/* Filled with the number of characters that
499				 * correspond to the bytes stored in the
500				 * output buffer. */
501{
502    CONST char *srcStart, *srcEnd;
503    char *dstEnd, *dstStart;
504    int result, numChars;
505
506    result = TCL_OK;
507
508    /* check alignment with ucs-2 (2 == sizeof(UCS-2)) */
509    if ((srcLen % 2) != 0) {
510	result = TCL_CONVERT_MULTIBYTE;
511	srcLen--;
512    }
513
514    srcStart = src;
515    srcEnd = src + srcLen;
516
517    dstStart = dst;
518    dstEnd = dst + dstLen - TCL_UTF_MAX;
519
520    for (numChars = 0; src < srcEnd; numChars++) {
521	if (dst > dstEnd) {
522	    result = TCL_CONVERT_NOSPACE;
523	    break;
524	}
525
526	/*
527	 * Need to swap byte-order on little-endian machines (x86) for
528	 * UCS-2BE. We know this is an LE->BE swap.
529	 */
530
531	dst += Tcl_UniCharToUtf(htons(*((short *)src)), dst);
532	src += 2 /* sizeof(UCS-2) */;
533    }
534
535    *srcReadPtr = src - srcStart;
536    *dstWrotePtr = dst - dstStart;
537    *dstCharsPtr = numChars;
538    return result;
539}
540
541/*
542 *-------------------------------------------------------------------------
543 *
544 * UtfToUcs2beProc --
545 *
546 *	Convert from UTF-8 to UCS-2BE (fixed 2-byte encoding).
547 *
548 * Results:
549 *	Returns TCL_OK if conversion was successful.
550 *
551 * Side effects:
552 *	None.
553 *
554 *-------------------------------------------------------------------------
555 */
556
557static int
558UtfToUcs2beProc(
559    ClientData clientData,	/* TableEncodingData that specifies
560				 * encoding. */
561    CONST char *src,		/* Source string in UTF-8. */
562    int srcLen,			/* Source string length in bytes. */
563    int flags,			/* Conversion control flags. */
564    Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
565				 * information used during a piecewise
566				 * conversion. Contents of statePtr are
567				 * initialized and/or reset by conversion
568				 * routine under control of flags argument. */
569    char *dst,			/* Output buffer in which converted string is
570				 * stored. */
571    int dstLen,			/* The maximum length of output buffer in
572				 * bytes. */
573    int *srcReadPtr,		/* Filled with the number of bytes from the
574				 * source string that were converted. This may
575				 * be less than the original source length if
576				 * there was a problem converting some source
577				 * characters. */
578    int *dstWrotePtr,		/* Filled with the number of bytes that were
579				 * stored in the output buffer as a result of
580				 * the conversion. */
581    int *dstCharsPtr)		/* Filled with the number of characters that
582				 * correspond to the bytes stored in the
583				 * output buffer. */
584{
585    CONST char *srcStart, *srcEnd, *srcClose, *dstStart, *dstEnd;
586    int result, numChars;
587    Tcl_UniChar ch;
588
589    srcStart = src;
590    srcEnd = src + srcLen;
591    srcClose = srcEnd;
592    if ((flags & TCL_ENCODING_END) == 0) {
593	srcClose -= TCL_UTF_MAX;
594    }
595
596    dstStart = dst;
597    dstEnd = dst + dstLen - 2 /* sizeof(UCS-2) */;
598
599    result = TCL_OK;
600    for (numChars = 0; src < srcEnd; numChars++) {
601	if ((src > srcClose) && (!Tcl_UtfCharComplete(src, srcEnd - src))) {
602	    /*
603	     * If there is more string to follow, this will ensure that the
604	     * last UTF-8 character in the source buffer hasn't been cut off.
605	     */
606
607	    result = TCL_CONVERT_MULTIBYTE;
608	    break;
609	}
610	if (dst > dstEnd) {
611	    result = TCL_CONVERT_NOSPACE;
612	    break;
613        }
614	src += Tcl_UtfToUniChar(src, &ch);
615
616	/*
617	 * Ensure big-endianness (store big bits first).
618	 * XXX: This hard-codes the assumed size of Tcl_UniChar as 2. Make
619	 * sure to work in char* for Tcl_UtfToUniChar alignment. [Bug 1122671]
620	 */
621
622	*dst++ = (ch >> 8);
623	*dst++ = (ch & 0xFF);
624    }
625    *srcReadPtr = src - srcStart;
626    *dstWrotePtr = dst - dstStart;
627    *dstCharsPtr = numChars;
628    return result;
629}
630#endif /* WORDS_BIGENDIAN */
631
632/*
633 *---------------------------------------------------------------------------
634 *
635 * TkpGetNativeFont --
636 *
637 *	Map a platform-specific native font name to a TkFont.
638 *
639 * Results:
640 *	The return value is a pointer to a TkFont that represents the native
641 *	font. If a native font by the given name could not be found, the
642 *	return value is NULL.
643 *
644 *	Every call to this function returns a new TkFont structure, even if
645 *	the name has already been seen before. The caller should call
646 *	TkpDeleteFont() when the font is no longer needed.
647 *
648 *	The caller is responsible for initializing the memory associated with
649 *	the generic TkFont when this function returns and releasing the
650 *	contents of the generic TkFont before calling TkpDeleteFont().
651 *
652 * Side effects:
653 *	Memory allocated.
654 *
655 *---------------------------------------------------------------------------
656 */
657
658TkFont *
659TkpGetNativeFont(
660    Tk_Window tkwin,		/* For display where font will be used. */
661    CONST char *name)		/* Platform-specific font name. */
662{
663    UnixFont *fontPtr;
664    XFontStruct *fontStructPtr;
665    FontAttributes fa;
666    CONST char *p;
667    int hasSpace, dashes, hasWild;
668
669    /*
670     * The behavior of X when given a name that isn't an XLFD is unspecified.
671     * For example, Exceed 6 returns a valid font for any random string. This
672     * is awkward since system names have higher priority than the other Tk
673     * font syntaxes. So, we need to perform a quick sanity check on the name
674     * and fail if it looks suspicious. We fail if the name:
675     *     - contains a space immediately before a dash
676     *	   - contains a space, but no '*' characters and fewer than 14 dashes
677     */
678
679    hasSpace = dashes = hasWild = 0;
680    for (p = name; *p != '\0'; p++) {
681	if (*p == ' ') {
682	    if (p[1] == '-') {
683		return NULL;
684	    }
685	    hasSpace = 1;
686	} else if (*p == '-') {
687	    dashes++;
688	} else if (*p == '*') {
689	    hasWild = 1;
690	}
691    }
692    if ((dashes < 14) && !hasWild && hasSpace) {
693	return NULL;
694    }
695
696    fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), name);
697    if (fontStructPtr == NULL) {
698	/*
699	 * Handle all names that look like XLFDs here. Otherwise, when
700	 * TkpGetFontFromAttributes is called from generic code, any foundry
701	 * or encoding information specified in the XLFD will have been parsed
702	 * out and lost. But make sure we don't have an "-option value" string
703	 * since TkFontParseXLFD would return a false success when attempting
704	 * to parse it.
705	 */
706
707	if (name[0] == '-') {
708	    if (name[1] != '*') {
709		char *dash;
710
711		dash = strchr(name + 1, '-');
712		if ((dash == NULL) || (isspace(UCHAR(dash[-1])))) {
713		    return NULL;
714		}
715	    }
716	} else if (name[0] != '*') {
717	    return NULL;
718	}
719	if (TkFontParseXLFD(name, &fa.fa, &fa.xa) != TCL_OK) {
720	    return NULL;
721	}
722	fontStructPtr = CreateClosestFont(tkwin, &fa.fa, &fa.xa);
723    }
724    fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont));
725    InitFont(tkwin, fontStructPtr, fontPtr);
726
727    return (TkFont *) fontPtr;
728}
729
730/*
731 *---------------------------------------------------------------------------
732 *
733 * TkpGetFontFromAttributes --
734 *
735 *	Given a desired set of attributes for a font, find a font with the
736 *	closest matching attributes.
737 *
738 * Results:
739 *	The return value is a pointer to a TkFont that represents the font
740 *	with the desired attributes. If a font with the desired attributes
741 *	could not be constructed, some other font will be substituted
742 *	automatically.
743 *
744 *	Every call to this function returns a new TkFont structure, even if
745 *	the specified attributes have already been seen before. The caller
746 *	should call TkpDeleteFont() to free the platform- specific data when
747 *	the font is no longer needed.
748 *
749 *	The caller is responsible for initializing the memory associated with
750 *	the generic TkFont when this function returns and releasing the
751 *	contents of the generic TkFont before calling TkpDeleteFont().
752 *
753 * Side effects:
754 *	Memory allocated.
755 *
756 *---------------------------------------------------------------------------
757 */
758
759TkFont *
760TkpGetFontFromAttributes(
761    TkFont *tkFontPtr,		/* If non-NULL, store the information in this
762				 * existing TkFont structure, rather than
763				 * allocating a new structure to hold the
764				 * font; the existing contents of the font
765				 * will be released. If NULL, a new TkFont
766				 * structure is allocated. */
767    Tk_Window tkwin,		/* For display where font will be used. */
768    CONST TkFontAttributes *faPtr)
769				/* Set of attributes to match. */
770{
771    UnixFont *fontPtr;
772    TkXLFDAttributes xa;
773    XFontStruct *fontStructPtr;
774
775    TkInitXLFDAttributes(&xa);
776    fontStructPtr = CreateClosestFont(tkwin, faPtr, &xa);
777
778    fontPtr = (UnixFont *) tkFontPtr;
779    if (fontPtr == NULL) {
780	fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont));
781    } else {
782	ReleaseFont(fontPtr);
783    }
784    InitFont(tkwin, fontStructPtr, fontPtr);
785
786    fontPtr->font.fa.underline = faPtr->underline;
787    fontPtr->font.fa.overstrike = faPtr->overstrike;
788
789    return (TkFont *) fontPtr;
790}
791
792/*
793 *---------------------------------------------------------------------------
794 *
795 * TkpDeleteFont --
796 *
797 *	Called to release a font allocated by TkpGetNativeFont() or
798 *	TkpGetFontFromAttributes(). The caller should have already released
799 *	the fields of the TkFont that are used exclusively by the generic
800 *	TkFont code.
801 *
802 * Results:
803 *	None.
804 *
805 * Side effects:
806 *	TkFont is deallocated.
807 *
808 *---------------------------------------------------------------------------
809 */
810
811void
812TkpDeleteFont(
813    TkFont *tkFontPtr)		/* Token of font to be deleted. */
814{
815    UnixFont *fontPtr = (UnixFont *) tkFontPtr;
816
817    ReleaseFont(fontPtr);
818}
819
820/*
821 *---------------------------------------------------------------------------
822 *
823 * TkpGetFontFamilies --
824 *
825 *	Return information about the font families that are available on the
826 *	display of the given window.
827 *
828 * Results:
829 *	Modifies interp's result object to hold a list of all the available
830 *	font families.
831 *
832 * Side effects:
833 *	None.
834 *
835 *---------------------------------------------------------------------------
836 */
837
838void
839TkpGetFontFamilies(
840    Tcl_Interp *interp,		/* Interp to hold result. */
841    Tk_Window tkwin)		/* For display to query. */
842{
843    int i, new, numNames;
844    char *family, **nameList;
845    Tcl_HashTable familyTable;
846    Tcl_HashEntry *hPtr;
847    Tcl_HashSearch search;
848    Tcl_Obj *resultPtr, *strPtr;
849
850    resultPtr = Tcl_GetObjResult(interp);
851
852    Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS);
853    nameList = ListFonts(Tk_Display(tkwin), "*", &numNames);
854    for (i = 0; i < numNames; i++) {
855	char *familyEnd;
856
857	family = strchr(nameList[i] + 1, '-');
858	if (family == NULL) {
859	    /*
860	     * Apparently, sometimes ListFonts() can return a font name with
861	     * zero or one '-' character in it. This is probably indicative of
862	     * a server misconfiguration, but crashing because of it is a very
863	     * bad idea anyway. [Bug 1475865]
864	     */
865
866	    continue;
867	}
868	family++;			/* Advance to char after '-'. */
869	familyEnd = strchr(family, '-');
870	if (familyEnd == NULL) {
871	    continue;			/* See comment above. */
872	}
873	*familyEnd = '\0';
874	Tcl_CreateHashEntry(&familyTable, family, &new);
875    }
876    XFreeFontNames(nameList);
877
878    hPtr = Tcl_FirstHashEntry(&familyTable, &search);
879    while (hPtr != NULL) {
880	strPtr = Tcl_NewStringObj(Tcl_GetHashKey(&familyTable, hPtr), -1);
881	Tcl_ListObjAppendElement(NULL, resultPtr, strPtr);
882	hPtr = Tcl_NextHashEntry(&search);
883    }
884
885    Tcl_DeleteHashTable(&familyTable);
886}
887
888/*
889 *-------------------------------------------------------------------------
890 *
891 * TkpGetSubFonts --
892 *
893 *	A function used by the testing package for querying the actual screen
894 *	fonts that make up a font object.
895 *
896 * Results:
897 *	Modifies interp's result object to hold a list containing the names of
898 *	the screen fonts that make up the given font object.
899 *
900 * Side effects:
901 *	None.
902 *
903 *-------------------------------------------------------------------------
904 */
905
906void
907TkpGetSubFonts(
908    Tcl_Interp *interp,
909    Tk_Font tkfont)
910{
911    int i;
912    Tcl_Obj *objv[3], *resultPtr, *listPtr;
913    UnixFont *fontPtr;
914    FontFamily *familyPtr;
915
916    resultPtr = Tcl_GetObjResult(interp);
917    fontPtr = (UnixFont *) tkfont;
918    for (i = 0; i < fontPtr->numSubFonts; i++) {
919	familyPtr = fontPtr->subFontArray[i].familyPtr;
920	objv[0] = Tcl_NewStringObj(familyPtr->faceName, -1);
921	objv[1] = Tcl_NewStringObj(familyPtr->foundry, -1);
922	objv[2] = Tcl_NewStringObj(
923		Tcl_GetEncodingName(familyPtr->encoding), -1);
924	listPtr = Tcl_NewListObj(3, objv);
925	Tcl_ListObjAppendElement(NULL, resultPtr, listPtr);
926    }
927}
928
929/*
930 *----------------------------------------------------------------------
931 *
932 * TkpGetFontAttrsForChar --
933 *
934 *	Retrieve the font attributes of the actual font used to render a given
935 *	character.
936 *
937 * Results:
938 *	None.
939 *
940 * Side effects:
941 *	The font attributes are stored in *faPtr.
942 *
943 *----------------------------------------------------------------------
944 */
945
946void
947TkpGetFontAttrsForChar(
948    Tk_Window tkwin,		/* Window on the font's display */
949    Tk_Font tkfont,		/* Font to query */
950    Tcl_UniChar c,		/* Character of interest */
951    TkFontAttributes *faPtr)	/* Output: Font attributes */
952{
953    FontAttributes atts;
954    UnixFont *fontPtr = (UnixFont *) tkfont;
955				/* Structure describing the logical font */
956    SubFont *lastSubFontPtr = &fontPtr->subFontArray[0];
957				/* Pointer to subfont array in case
958				 * FindSubFontForChar needs to fix up the
959				 * memory allocation */
960    SubFont *thisSubFontPtr = FindSubFontForChar(fontPtr, c, &lastSubFontPtr);
961				/* Pointer to the subfont to use for the given
962				 * character */
963    GetFontAttributes(Tk_Display(tkwin), thisSubFontPtr->fontStructPtr, &atts);
964    *faPtr = atts.fa;
965}
966
967/*
968 *---------------------------------------------------------------------------
969 *
970 * Tk_MeasureChars --
971 *
972 *	Determine the number of characters from the string that will fit in
973 *	the given horizontal span. The measurement is done under the
974 *	assumption that Tk_DrawChars() will be used to actually display the
975 *	characters.
976 *
977 * Results:
978 *	The return value is the number of bytes from source that fit into the
979 *	span that extends from 0 to maxLength. *lengthPtr is filled with the
980 *	x-coordinate of the right edge of the last character that did fit.
981 *
982 * Side effects:
983 *	None.
984 *
985 *---------------------------------------------------------------------------
986 */
987
988int
989Tk_MeasureChars(
990    Tk_Font tkfont,		/* Font in which characters will be drawn. */
991    CONST char *source,		/* UTF-8 string to be displayed. Need not be
992				 * '\0' terminated. */
993    int numBytes,		/* Maximum number of bytes to consider from
994				 * source string. */
995    int maxLength,		/* If >= 0, maxLength specifies the longest
996				 * permissible line length in pixels; don't
997				 * consider any character that would cross
998				 * this x-position. If < 0, then line length
999				 * is unbounded and the flags argument is
1000				 * ignored. */
1001    int flags,			/* Various flag bits OR-ed together:
1002				 * TK_PARTIAL_OK means include the last char
1003				 * which only partially fit on this line.
1004				 * TK_WHOLE_WORDS means stop on a word
1005				 * boundary, if possible. TK_AT_LEAST_ONE
1006				 * means return at least one character even if
1007				 * no characters fit. */
1008    int *lengthPtr)		/* Filled with x-location just after the
1009				 * terminating character. */
1010{
1011    UnixFont *fontPtr;
1012    SubFont *lastSubFontPtr;
1013    int curX, curByte;
1014
1015    /*
1016     * Unix does not use kerning or fractional character widths when
1017     * displaying text on the screen. So that means we can safely measure
1018     * individual characters or spans of characters and add up the widths w/o
1019     * any "off-by-one-pixel" errors.
1020     */
1021
1022    fontPtr = (UnixFont *) tkfont;
1023
1024    lastSubFontPtr = &fontPtr->subFontArray[0];
1025
1026    if (numBytes == 0) {
1027	curX = 0;
1028	curByte = 0;
1029    } else if (maxLength < 0) {
1030	CONST char *p, *end, *next;
1031	Tcl_UniChar ch;
1032	SubFont *thisSubFontPtr;
1033	FontFamily *familyPtr;
1034	Tcl_DString runString;
1035
1036	/*
1037	 * A three step process:
1038	 * 1. Find a contiguous range of characters that can all be
1039	 *    represented by a single screen font.
1040	 * 2. Convert those chars to the encoding of that font.
1041	 * 3. Measure converted chars.
1042	 */
1043
1044	curX = 0;
1045	end = source + numBytes;
1046	for (p = source; p < end; ) {
1047	    next = p + Tcl_UtfToUniChar(p, &ch);
1048	    thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr);
1049	    if (thisSubFontPtr != lastSubFontPtr) {
1050		familyPtr = lastSubFontPtr->familyPtr;
1051		Tcl_UtfToExternalDString(familyPtr->encoding, source,
1052			p - source, &runString);
1053		if (familyPtr->isTwoByteFont) {
1054		    curX += XTextWidth16(lastSubFontPtr->fontStructPtr,
1055			    (XChar2b *) Tcl_DStringValue(&runString),
1056			    Tcl_DStringLength(&runString) / 2);
1057		} else {
1058		    curX += XTextWidth(lastSubFontPtr->fontStructPtr,
1059			    Tcl_DStringValue(&runString),
1060			    Tcl_DStringLength(&runString));
1061		}
1062		Tcl_DStringFree(&runString);
1063		lastSubFontPtr = thisSubFontPtr;
1064		source = p;
1065	    }
1066	    p = next;
1067	}
1068	familyPtr = lastSubFontPtr->familyPtr;
1069	Tcl_UtfToExternalDString(familyPtr->encoding, source, p - source,
1070		&runString);
1071	if (familyPtr->isTwoByteFont) {
1072	    curX += XTextWidth16(lastSubFontPtr->fontStructPtr,
1073		    (XChar2b *) Tcl_DStringValue(&runString),
1074		    Tcl_DStringLength(&runString) >> 1);
1075	} else {
1076	    curX += XTextWidth(lastSubFontPtr->fontStructPtr,
1077		    Tcl_DStringValue(&runString),
1078		    Tcl_DStringLength(&runString));
1079	}
1080	Tcl_DStringFree(&runString);
1081	curByte = numBytes;
1082    } else {
1083	CONST char *p, *end, *next, *term;
1084	int newX, termX, sawNonSpace, dstWrote;
1085	Tcl_UniChar ch;
1086	FontFamily *familyPtr;
1087	char buf[16];
1088
1089	/*
1090	 * How many chars will fit in the space allotted? This first version
1091	 * may be inefficient because it measures every character
1092	 * individually.
1093	 */
1094
1095	next = source + Tcl_UtfToUniChar(source, &ch);
1096	newX = curX = termX = 0;
1097
1098	term = source;
1099	end = source + numBytes;
1100
1101	sawNonSpace = (ch > 255) || !isspace(ch);
1102	familyPtr = lastSubFontPtr->familyPtr;
1103	for (p = source; ; ) {
1104	    if ((ch < BASE_CHARS) && (fontPtr->widths[ch] != 0)) {
1105		newX += fontPtr->widths[ch];
1106	    } else {
1107		lastSubFontPtr = FindSubFontForChar(fontPtr, ch, NULL);
1108		familyPtr = lastSubFontPtr->familyPtr;
1109		Tcl_UtfToExternal(NULL, familyPtr->encoding, p, next - p,
1110			0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL);
1111		if (familyPtr->isTwoByteFont) {
1112		    newX += XTextWidth16(lastSubFontPtr->fontStructPtr,
1113			    (XChar2b *) buf, dstWrote >> 1);
1114		} else {
1115		    newX += XTextWidth(lastSubFontPtr->fontStructPtr, buf,
1116			    dstWrote);
1117		}
1118	    }
1119	    if (newX > maxLength) {
1120		break;
1121	    }
1122	    curX = newX;
1123	    p = next;
1124	    if (p >= end) {
1125		term = end;
1126		termX = curX;
1127		break;
1128	    }
1129
1130	    next += Tcl_UtfToUniChar(next, &ch);
1131	    if ((ch < 256) && isspace(ch)) {
1132		if (sawNonSpace) {
1133		    term = p;
1134		    termX = curX;
1135		    sawNonSpace = 0;
1136		}
1137	    } else {
1138		sawNonSpace = 1;
1139	    }
1140	}
1141
1142	/*
1143	 * P points to the first character that doesn't fit in the desired
1144	 * span. Use the flags to figure out what to return.
1145	 */
1146
1147	if ((flags & TK_PARTIAL_OK) && (p < end) && (curX < maxLength)) {
1148	    /*
1149	     * Include the first character that didn't quite fit in the
1150	     * desired span. The width returned will include the width of that
1151	     * extra character.
1152	     */
1153
1154	    curX = newX;
1155	    p += Tcl_UtfToUniChar(p, &ch);
1156	}
1157	if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) {
1158	    term = p;
1159	    termX = curX;
1160	    if (term == source) {
1161		term += Tcl_UtfToUniChar(term, &ch);
1162		termX = newX;
1163	    }
1164	} else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) {
1165	    term = p;
1166	    termX = curX;
1167	}
1168
1169	curX = termX;
1170	curByte = term - source;
1171    }
1172
1173    *lengthPtr = curX;
1174    return curByte;
1175}
1176
1177/*
1178 *---------------------------------------------------------------------------
1179 *
1180 * TkpMeasureCharsInContext --
1181 *
1182 *	Determine the number of bytes from the string that will fit in the
1183 *	given horizontal span. The measurement is done under the assumption
1184 *	that TkpDrawCharsInContext() will be used to actually display the
1185 *	characters.
1186 *
1187 *	This one is almost the same as Tk_MeasureChars(), but with access to
1188 *	all the characters on the line for context. On X11 this context isn't
1189 *	consulted, so we just call Tk_MeasureChars().
1190 *
1191 * Results:
1192 *	The return value is the number of bytes from source that fit into the
1193 *	span that extends from 0 to maxLength. *lengthPtr is filled with the
1194 *	x-coordinate of the right edge of the last character that did fit.
1195 *
1196 * Side effects:
1197 *	None.
1198 *
1199 *---------------------------------------------------------------------------
1200 */
1201
1202int
1203TkpMeasureCharsInContext(
1204    Tk_Font tkfont,		/* Font in which characters will be drawn. */
1205    CONST char *source,		/* UTF-8 string to be displayed. Need not be
1206				 * '\0' terminated. */
1207    int numBytes,		/* Maximum number of bytes to consider from
1208				 * source string in all. */
1209    int rangeStart,		/* Index of first byte to measure. */
1210    int rangeLength,		/* Length of range to measure in bytes. */
1211    int maxLength,		/* If >= 0, maxLength specifies the longest
1212				 * permissible line length; don't consider any
1213				 * character that would cross this x-position.
1214				 * If < 0, then line length is unbounded and
1215				 * the flags argument is ignored. */
1216    int flags,			/* Various flag bits OR-ed together:
1217				 * TK_PARTIAL_OK means include the last char
1218				 * which only partially fit on this line.
1219				 * TK_WHOLE_WORDS means stop on a word
1220				 * boundary, if possible. TK_AT_LEAST_ONE
1221				 * means return at least one character even if
1222				 * no characters fit. TK_ISOLATE_END means
1223				 * that the last character should not be
1224				 * considered in context with the rest of the
1225				 * string (used for breaking lines). */
1226    int *lengthPtr)		/* Filled with x-location just after the
1227				 * terminating character. */
1228{
1229    (void) numBytes; /*unused*/
1230    return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength,
1231	    maxLength, flags, lengthPtr);
1232}
1233
1234/*
1235 *---------------------------------------------------------------------------
1236 *
1237 * Tk_DrawChars --
1238 *
1239 *	Draw a string of characters on the screen. Tk_DrawChars() expands
1240 *	control characters that occur in the string to \xNN sequences.
1241 *
1242 * Results:
1243 *	None.
1244 *
1245 * Side effects:
1246 *	Information gets drawn on the screen.
1247 *
1248 *---------------------------------------------------------------------------
1249 */
1250
1251void
1252Tk_DrawChars(
1253    Display *display,		/* Display on which to draw. */
1254    Drawable drawable,		/* Window or pixmap in which to draw. */
1255    GC gc,			/* Graphics context for drawing characters. */
1256    Tk_Font tkfont,		/* Font in which characters will be drawn;
1257				 * must be the same as font used in GC. */
1258    CONST char *source,		/* UTF-8 string to be displayed. Need not be
1259				 * '\0' terminated. All Tk meta-characters
1260				 * (tabs, control characters, and newlines)
1261				 * should be stripped out of the string that
1262				 * is passed to this function. If they are not
1263				 * stripped out, they will be displayed as
1264				 * regular printing characters. */
1265    int numBytes,		/* Number of bytes in string. */
1266    int x, int y)		/* Coordinates at which to place origin of
1267				 * string when drawing. */
1268{
1269    UnixFont *fontPtr;
1270    SubFont *thisSubFontPtr, *lastSubFontPtr;
1271    Tcl_DString runString;
1272    CONST char *p, *end, *next;
1273    int xStart, needWidth, window_width, do_width;
1274    Tcl_UniChar ch;
1275    FontFamily *familyPtr;
1276#ifdef TK_DRAW_CHAR_XWINDOW_CHECK
1277    int rx, ry;
1278    unsigned int width, height, border_width, depth;
1279    Drawable root;
1280#endif
1281
1282    fontPtr = (UnixFont *) tkfont;
1283    lastSubFontPtr = &fontPtr->subFontArray[0];
1284
1285    xStart = x;
1286
1287#ifdef TK_DRAW_CHAR_XWINDOW_CHECK
1288    /*
1289     * Get the window width so we can abort drawing outside of the window
1290     */
1291
1292    if (XGetGeometry(display, drawable, &root, &rx, &ry, &width, &height,
1293	    &border_width, &depth) == False) {
1294	window_width = INT_MAX;
1295    } else {
1296	window_width = width;
1297    }
1298#else
1299    /*
1300     * This is used by default until we find a solution that doesn't do a
1301     * round-trip to the X server (needed to get Tk cached window width).
1302     */
1303
1304    window_width = 32768;
1305#endif
1306
1307    end = source + numBytes;
1308    needWidth = fontPtr->font.fa.underline + fontPtr->font.fa.overstrike;
1309    for (p = source; p <= end; ) {
1310	if (p < end) {
1311	    next = p + Tcl_UtfToUniChar(p, &ch);
1312	    thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr);
1313	} else {
1314	    next = p + 1;
1315	    thisSubFontPtr = lastSubFontPtr;
1316	}
1317	if ((thisSubFontPtr != lastSubFontPtr)
1318		|| (p == end) || (p-source > 200)) {
1319	    if (p > source) {
1320		do_width = (needWidth || (p != end)) ? 1 : 0;
1321		familyPtr = lastSubFontPtr->familyPtr;
1322
1323		Tcl_UtfToExternalDString(familyPtr->encoding, source,
1324			p - source, &runString);
1325		if (familyPtr->isTwoByteFont) {
1326		    XDrawString16(display, drawable, gc, x, y,
1327			    (XChar2b *) Tcl_DStringValue(&runString),
1328			    Tcl_DStringLength(&runString) / 2);
1329		    if (do_width) {
1330			x += XTextWidth16(lastSubFontPtr->fontStructPtr,
1331				(XChar2b *) Tcl_DStringValue(&runString),
1332				Tcl_DStringLength(&runString) / 2);
1333		    }
1334		} else {
1335		    XDrawString(display, drawable, gc, x, y,
1336			    Tcl_DStringValue(&runString),
1337			    Tcl_DStringLength(&runString));
1338		    if (do_width) {
1339			x += XTextWidth(lastSubFontPtr->fontStructPtr,
1340				Tcl_DStringValue(&runString),
1341				Tcl_DStringLength(&runString));
1342		    }
1343		}
1344		Tcl_DStringFree(&runString);
1345	    }
1346	    lastSubFontPtr = thisSubFontPtr;
1347	    source = p;
1348	    XSetFont(display, gc, lastSubFontPtr->fontStructPtr->fid);
1349	    if (x > window_width) {
1350		break;
1351	    }
1352	}
1353	p = next;
1354    }
1355
1356    if (lastSubFontPtr != &fontPtr->subFontArray[0]) {
1357	XSetFont(display, gc, fontPtr->subFontArray[0].fontStructPtr->fid);
1358    }
1359
1360    if (fontPtr->font.fa.underline != 0) {
1361	XFillRectangle(display, drawable, gc, xStart,
1362		y + fontPtr->underlinePos,
1363		(unsigned) (x - xStart), (unsigned) fontPtr->barHeight);
1364    }
1365    if (fontPtr->font.fa.overstrike != 0) {
1366	y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
1367	XFillRectangle(display, drawable, gc, xStart, y,
1368		(unsigned) (x - xStart), (unsigned) fontPtr->barHeight);
1369    }
1370}
1371
1372/*
1373 *---------------------------------------------------------------------------
1374 *
1375 * TkpDrawCharsInContext --
1376 *
1377 *	Draw a string of characters on the screen like Tk_DrawChars(), but
1378 *	with access to all the characters on the line for context. On X11 this
1379 *	context isn't consulted, so we just call Tk_DrawChars().
1380 *
1381 * Results:
1382 *	None.
1383 *
1384 * Side effects:
1385 *	Information gets drawn on the screen.
1386 *
1387 *---------------------------------------------------------------------------
1388 */
1389
1390void
1391TkpDrawCharsInContext(
1392    Display *display,		/* Display on which to draw. */
1393    Drawable drawable,		/* Window or pixmap in which to draw. */
1394    GC gc,			/* Graphics context for drawing characters. */
1395    Tk_Font tkfont,		/* Font in which characters will be drawn;
1396				 * must be the same as font used in GC. */
1397    CONST char *source,		/* UTF-8 string to be displayed. Need not be
1398				 * '\0' terminated. All Tk meta-characters
1399				 * (tabs, control characters, and newlines)
1400				 * should be stripped out of the string that
1401				 * is passed to this function. If they are not
1402				 * stripped out, they will be displayed as
1403				 * regular printing characters. */
1404    int numBytes,		/* Number of bytes in string. */
1405    int rangeStart,		/* Index of first byte to draw. */
1406    int rangeLength,		/* Length of range to draw in bytes. */
1407    int x, int y)		/* Coordinates at which to place origin of the
1408				 * whole (not just the range) string when
1409				 * drawing. */
1410{
1411    (void) numBytes; /*unused*/
1412
1413    Tk_DrawChars(display, drawable, gc, tkfont, source + rangeStart,
1414	    rangeLength, x, y);
1415}
1416
1417/*
1418 *-------------------------------------------------------------------------
1419 *
1420 * CreateClosestFont --
1421 *
1422 *	Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). Given a
1423 *	set of font attributes, construct a close XFontStruct. If requested
1424 *	face name is not available, automatically substitutes an alias for
1425 *	requested face name. If encoding is not specified (or the requested
1426 *	one is not available), automatically chooses another encoding from the
1427 *	list of preferred encodings. If the foundry is not specified (or is
1428 *	not available) automatically prefers "adobe" foundry. For all other
1429 *	attributes, if the requested value was not available, the appropriate
1430 *	"close" value will be used.
1431 *
1432 * Results:
1433 *	Return value is the XFontStruct that best matched the requested
1434 *	attributes. The return value is never NULL; some font will always be
1435 *	returned.
1436 *
1437 * Side effects:
1438 *	None.
1439 *
1440 *-------------------------------------------------------------------------
1441 */
1442
1443static XFontStruct *
1444CreateClosestFont(
1445    Tk_Window tkwin,		/* For display where font will be used. */
1446    CONST TkFontAttributes *faPtr,
1447				/* Set of generic attributes to match. */
1448    CONST TkXLFDAttributes *xaPtr)
1449				/* Set of X-specific attributes to match. */
1450{
1451    FontAttributes want;
1452    char **nameList;
1453    int numNames, nameIdx, bestIdx[2];
1454    Display *display;
1455    XFontStruct *fontStructPtr;
1456    unsigned int bestScore[2];
1457
1458    want.fa = *faPtr;
1459    want.xa = *xaPtr;
1460
1461    if (want.xa.foundry == NULL) {
1462	want.xa.foundry = Tk_GetUid("adobe");
1463    }
1464    if (want.fa.family == NULL) {
1465	want.fa.family = Tk_GetUid("fixed");
1466    }
1467    want.fa.size = -TkFontGetPixels(tkwin, faPtr->size);
1468    if (want.xa.charset == NULL || *want.xa.charset == '\0') {
1469	want.xa.charset = Tk_GetUid("iso8859-1");	/* locale. */
1470    }
1471
1472    display = Tk_Display(tkwin);
1473
1474    /*
1475     * Algorithm to get the closest font to the name requested.
1476     *
1477     * try fontname
1478     * try all aliases for fontname
1479     * foreach fallback for fontname
1480     *	    try the fallback
1481     *	    try all aliases for the fallback
1482     */
1483
1484    nameList = ListFontOrAlias(display, want.fa.family, &numNames);
1485    if (numNames == 0) {
1486	char ***fontFallbacks;
1487	int i, j;
1488	char *fallback;
1489
1490	fontFallbacks = TkFontGetFallbacks();
1491	for (i = 0; fontFallbacks[i] != NULL; i++) {
1492	    for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
1493		if (strcasecmp(want.fa.family, fallback) == 0) {
1494		    break;
1495		}
1496	    }
1497	    if (fallback != NULL) {
1498		for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
1499		    nameList = ListFontOrAlias(display, fallback, &numNames);
1500		    if (numNames != 0) {
1501			goto found;
1502		    }
1503		}
1504	    }
1505	}
1506	nameList = ListFonts(display, "fixed", &numNames);
1507	if (numNames == 0) {
1508	    nameList = ListFonts(display, "*", &numNames);
1509	}
1510	if (numNames == 0) {
1511	    return GetSystemFont(display);
1512	}
1513    }
1514
1515  found:
1516    bestIdx[0] = -1;
1517    bestIdx[1] = -1;
1518    bestScore[0] = (unsigned int) -1;
1519    bestScore[1] = (unsigned int) -1;
1520    for (nameIdx = 0; nameIdx < numNames; nameIdx++) {
1521	FontAttributes got;
1522	int scalable;
1523	unsigned int score;
1524
1525	if (TkFontParseXLFD(nameList[nameIdx], &got.fa, &got.xa) != TCL_OK) {
1526	    continue;
1527	}
1528	IdentifySymbolEncodings(&got);
1529	scalable = (got.fa.size == 0);
1530	score = RankAttributes(&want, &got);
1531	if (score < bestScore[scalable]) {
1532	    bestIdx[scalable] = nameIdx;
1533	    bestScore[scalable] = score;
1534	}
1535	if (score == 0) {
1536	    break;
1537	}
1538    }
1539
1540    fontStructPtr = GetScreenFont(display, &want, nameList, bestIdx,
1541	    bestScore);
1542    XFreeFontNames(nameList);
1543
1544    if (fontStructPtr == NULL) {
1545	return GetSystemFont(display);
1546    }
1547    return fontStructPtr;
1548}
1549
1550/*
1551 *---------------------------------------------------------------------------
1552 *
1553 * InitFont --
1554 *
1555 *	Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
1556 *	Initializes the memory for a new UnixFont that wraps the
1557 *	platform-specific data.
1558 *
1559 *	The caller is responsible for initializing the fields of the TkFont
1560 *	that are used exclusively by the generic TkFont code, and for
1561 *	releasing those fields before calling TkpDeleteFont().
1562 *
1563 * Results:
1564 *	Fills the WinFont structure.
1565 *
1566 * Side effects:
1567 *	Memory allocated.
1568 *
1569 *---------------------------------------------------------------------------
1570 */
1571
1572static void
1573InitFont(
1574    Tk_Window tkwin,		/* For screen where font will be used. */
1575    XFontStruct *fontStructPtr,	/* X information about font. */
1576    UnixFont *fontPtr)		/* Filled with information constructed from
1577				 * the above arguments. */
1578{
1579    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1580	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1581    unsigned long value;
1582    int minHi, maxHi, minLo, maxLo, fixed, width, limit, i, n;
1583    FontAttributes fa;
1584    TkFontAttributes *faPtr;
1585    TkFontMetrics *fmPtr;
1586    SubFont *controlPtr, *subFontPtr;
1587    char *pageMap;
1588    Display *display;
1589
1590    /*
1591     * Get all font attributes and metrics.
1592     */
1593
1594    display = Tk_Display(tkwin);
1595    GetFontAttributes(display, fontStructPtr, &fa);
1596
1597    minHi = fontStructPtr->min_byte1;
1598    maxHi = fontStructPtr->max_byte1;
1599    minLo = fontStructPtr->min_char_or_byte2;
1600    maxLo = fontStructPtr->max_char_or_byte2;
1601
1602    fixed = 1;
1603    if (fontStructPtr->per_char != NULL) {
1604	width = 0;
1605	limit = (maxHi - minHi + 1) * (maxLo - minLo + 1);
1606	for (i = 0; i < limit; i++) {
1607	    n = fontStructPtr->per_char[i].width;
1608	    if (n != 0) {
1609		if (width == 0) {
1610		    width = n;
1611		} else if (width != n) {
1612		    fixed = 0;
1613		    break;
1614		}
1615	    }
1616	}
1617    }
1618
1619    fontPtr->font.fid = fontStructPtr->fid;
1620
1621    faPtr = &fontPtr->font.fa;
1622    faPtr->family = fa.fa.family;
1623    faPtr->size = TkFontGetPoints(tkwin, fa.fa.size);
1624    faPtr->weight = fa.fa.weight;
1625    faPtr->slant = fa.fa.slant;
1626    faPtr->underline = 0;
1627    faPtr->overstrike = 0;
1628
1629    fmPtr = &fontPtr->font.fm;
1630    fmPtr->ascent = fontStructPtr->ascent;
1631    fmPtr->descent = fontStructPtr->descent;
1632    fmPtr->maxWidth = fontStructPtr->max_bounds.width;
1633    fmPtr->fixed = fixed;
1634
1635    fontPtr->display = display;
1636    fontPtr->pixelSize = TkFontGetPixels(tkwin, fa.fa.size);
1637    fontPtr->xa = fa.xa;
1638
1639    fontPtr->numSubFonts = 1;
1640    fontPtr->subFontArray = fontPtr->staticSubFonts;
1641    InitSubFont(display, fontStructPtr, 1, &fontPtr->subFontArray[0]);
1642
1643    fontPtr->controlSubFont = fontPtr->subFontArray[0];
1644    subFontPtr = FindSubFontForChar(fontPtr, '0', NULL);
1645    controlPtr = &fontPtr->controlSubFont;
1646    controlPtr->fontStructPtr = subFontPtr->fontStructPtr;
1647    controlPtr->familyPtr = &tsdPtr->controlFamily;
1648    controlPtr->fontMap = tsdPtr->controlFamily.fontMap;
1649
1650    pageMap = fontPtr->subFontArray[0].fontMap[0];
1651    for (i = 0; i < 256; i++) {
1652	if ((minHi > 0) || (i < minLo) || (i > maxLo)
1653		|| (((pageMap[i>>3] >> (i&7)) & 1) == 0)) {
1654	    n = 0;
1655	} else if (fontStructPtr->per_char == NULL) {
1656	    n = fontStructPtr->max_bounds.width;
1657	} else {
1658	    n = fontStructPtr->per_char[i - minLo].width;
1659	}
1660	fontPtr->widths[i] = n;
1661    }
1662
1663    if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
1664	fontPtr->underlinePos = value;
1665    } else {
1666	/*
1667	 * If the XA_UNDERLINE_POSITION property does not exist, the X manual
1668	 * recommends using the following value:
1669	 */
1670
1671	fontPtr->underlinePos = fontStructPtr->descent / 2;
1672    }
1673    fontPtr->barHeight = 0;
1674    if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
1675	fontPtr->barHeight = value;
1676    }
1677    if (fontPtr->barHeight == 0) {
1678	/*
1679	 * If the XA_UNDERLINE_THICKNESS property does not exist, the X manual
1680	 * recommends using the width of the stem on a capital letter. I don't
1681	 * know of a way to get the stem width of a letter, so guess and use
1682	 * 1/3 the width of a capital I.
1683	 */
1684
1685	fontPtr->barHeight = fontPtr->widths['I'] / 3;
1686	if (fontPtr->barHeight == 0) {
1687	    fontPtr->barHeight = 1;
1688	}
1689    }
1690    if (fontPtr->underlinePos + fontPtr->barHeight > fontStructPtr->descent) {
1691	/*
1692	 * If this set of cobbled together values would cause the bottom of
1693	 * the underline bar to stick below the descent of the font, jack the
1694	 * underline up a bit higher.
1695	 */
1696
1697	fontPtr->barHeight = fontStructPtr->descent - fontPtr->underlinePos;
1698	if (fontPtr->barHeight == 0) {
1699	    fontPtr->underlinePos--;
1700	    fontPtr->barHeight = 1;
1701	}
1702    }
1703}
1704
1705/*
1706 *-------------------------------------------------------------------------
1707 *
1708 * ReleaseFont --
1709 *
1710 *	Called to release the unix-specific contents of a TkFont. The caller
1711 *	is responsible for freeing the memory used by the font itself.
1712 *
1713 * Results:
1714 *	None.
1715 *
1716 * Side effects:
1717 *	Memory is freed.
1718 *
1719 *---------------------------------------------------------------------------
1720 */
1721
1722static void
1723ReleaseFont(
1724    UnixFont *fontPtr)		/* The font to delete. */
1725{
1726    int i;
1727
1728    for (i = 0; i < fontPtr->numSubFonts; i++) {
1729	ReleaseSubFont(fontPtr->display, &fontPtr->subFontArray[i]);
1730    }
1731    if (fontPtr->subFontArray != fontPtr->staticSubFonts) {
1732	ckfree((char *) fontPtr->subFontArray);
1733    }
1734}
1735
1736/*
1737 *-------------------------------------------------------------------------
1738 *
1739 * InitSubFont --
1740 *
1741 *	Wrap a screen font and load the FontFamily that represents it. Used to
1742 *	prepare a SubFont so that characters can be mapped from UTF-8 to the
1743 *	charset of the font.
1744 *
1745 * Results:
1746 *	The subFontPtr is filled with information about the font.
1747 *
1748 * Side effects:
1749 *	None.
1750 *
1751 *-------------------------------------------------------------------------
1752 */
1753
1754static void
1755InitSubFont(
1756    Display *display,		/* Display in which font will be used. */
1757    XFontStruct *fontStructPtr,	/* The screen font. */
1758    int base,			/* Non-zero if this SubFont is being used as
1759				 * the base font for a font object. */
1760    SubFont *subFontPtr)	/* Filled with SubFont constructed from above
1761				 * attributes. */
1762{
1763    subFontPtr->fontStructPtr = fontStructPtr;
1764    subFontPtr->familyPtr = AllocFontFamily(display, fontStructPtr, base);
1765    subFontPtr->fontMap = subFontPtr->familyPtr->fontMap;
1766}
1767
1768/*
1769 *-------------------------------------------------------------------------
1770 *
1771 * ReleaseSubFont --
1772 *
1773 *	Called to release the contents of a SubFont. The caller is responsible
1774 *	for freeing the memory used by the SubFont itself.
1775 *
1776 * Results:
1777 *	None.
1778 *
1779 * Side effects:
1780 *	Memory and resources are freed.
1781 *
1782 *---------------------------------------------------------------------------
1783 */
1784
1785static void
1786ReleaseSubFont(
1787    Display *display,		/* Display which owns screen font. */
1788    SubFont *subFontPtr)	/* The SubFont to delete. */
1789{
1790    XFreeFont(display, subFontPtr->fontStructPtr);
1791    FreeFontFamily(subFontPtr->familyPtr);
1792}
1793
1794/*
1795 *-------------------------------------------------------------------------
1796 *
1797 * AllocFontFamily --
1798 *
1799 *	Find the FontFamily structure associated with the given font name.
1800 *	The information should be stored by the caller in a SubFont and used
1801 *	when determining if that SubFont supports a character.
1802 *
1803 *	Cannot use the string name used to construct the font as the key,
1804 *	because the capitalization may not be canonical. Therefore use the
1805 *	face name actually retrieved from the font metrics as the key.
1806 *
1807 * Results:
1808 *	A pointer to a FontFamily. The reference count in the FontFamily is
1809 *	automatically incremented. When the SubFont is released, the reference
1810 *	count is decremented. When no SubFont is using this FontFamily, it may
1811 *	be deleted.
1812 *
1813 * Side effects:
1814 *	A new FontFamily structure will be allocated if this font family has
1815 *	not been seen. TrueType character existence metrics are loaded into
1816 *	the FontFamily structure.
1817 *
1818 *-------------------------------------------------------------------------
1819 */
1820
1821static FontFamily *
1822AllocFontFamily(
1823    Display *display,		/* Display in which font will be used. */
1824    XFontStruct *fontStructPtr,	/* Screen font whose FontFamily is to be
1825				 * returned. */
1826    int base)			/* Non-zero if this font family is to be used
1827				 * in the base font of a font object. */
1828{
1829    FontFamily *familyPtr;
1830    FontAttributes fa;
1831    Tcl_Encoding encoding;
1832    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1833	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1834
1835    GetFontAttributes(display, fontStructPtr, &fa);
1836    encoding = Tcl_GetEncoding(NULL, GetEncodingAlias(fa.xa.charset));
1837
1838    familyPtr = tsdPtr->fontFamilyList;
1839    for (; familyPtr != NULL; familyPtr = familyPtr->nextPtr) {
1840	if ((familyPtr->faceName == fa.fa.family)
1841		&& (familyPtr->foundry == fa.xa.foundry)
1842		&& (familyPtr->encoding == encoding)) {
1843	    Tcl_FreeEncoding(encoding);
1844	    familyPtr->refCount++;
1845	    return familyPtr;
1846	}
1847    }
1848
1849    familyPtr = (FontFamily *) ckalloc(sizeof(FontFamily));
1850    memset(familyPtr, 0, sizeof(FontFamily));
1851    familyPtr->nextPtr = tsdPtr->fontFamilyList;
1852    tsdPtr->fontFamilyList = familyPtr;
1853
1854    /*
1855     * Set key for this FontFamily.
1856     */
1857
1858    familyPtr->foundry = fa.xa.foundry;
1859    familyPtr->faceName = fa.fa.family;
1860    familyPtr->encoding = encoding;
1861
1862    /*
1863     * An initial refCount of 2 means that FontFamily information will persist
1864     * even when the SubFont that loaded the FontFamily is released. Change it
1865     * to 1 to cause FontFamilies to be unloaded when not in use.
1866     */
1867
1868    familyPtr->refCount = 2;
1869
1870    /*
1871     * One byte/character fonts have both min_byte1 and max_byte1 0, and
1872     * max_char_or_byte2 <= 255. Anything else specifies a two byte/character
1873     * font.
1874     */
1875
1876    familyPtr->isTwoByteFont = !(
1877	    (fontStructPtr->min_byte1 == 0) &&
1878	    (fontStructPtr->max_byte1 == 0) &&
1879	    (fontStructPtr->max_char_or_byte2 < 256));
1880    return familyPtr;
1881}
1882
1883/*
1884 *-------------------------------------------------------------------------
1885 *
1886 * FreeFontFamily --
1887 *
1888 *	Called to free an FontFamily when the SubFont is finished using it.
1889 *	Frees the contents of the FontFamily and the memory used by the
1890 *	FontFamily itself.
1891 *
1892 * Results:
1893 *	None.
1894 *
1895 * Side effects:
1896 *	None.
1897 *
1898 *-------------------------------------------------------------------------
1899 */
1900
1901static void
1902FreeFontFamily(
1903    FontFamily *familyPtr)	/* The FontFamily to delete. */
1904{
1905    FontFamily **familyPtrPtr;
1906    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1907	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1908    int i;
1909
1910    if (familyPtr == NULL) {
1911	return;
1912    }
1913    familyPtr->refCount--;
1914    if (familyPtr->refCount > 0) {
1915	return;
1916    }
1917    Tcl_FreeEncoding(familyPtr->encoding);
1918    for (i = 0; i < FONTMAP_PAGES; i++) {
1919	if (familyPtr->fontMap[i] != NULL) {
1920	    ckfree(familyPtr->fontMap[i]);
1921	}
1922    }
1923
1924    /*
1925     * Delete from list.
1926     */
1927
1928    for (familyPtrPtr = &tsdPtr->fontFamilyList; ; ) {
1929	if (*familyPtrPtr == familyPtr) {
1930	    *familyPtrPtr = familyPtr->nextPtr;
1931	    break;
1932	}
1933	familyPtrPtr = &(*familyPtrPtr)->nextPtr;
1934    }
1935
1936    ckfree((char *) familyPtr);
1937}
1938
1939/*
1940 *-------------------------------------------------------------------------
1941 *
1942 * FindSubFontForChar --
1943 *
1944 *	Determine which screen font is necessary to use to display the given
1945 *	character. If the font object does not have a screen font that can
1946 *	display the character, another screen font may be loaded into the font
1947 *	object, following a set of preferred fallback rules.
1948 *
1949 * Results:
1950 *	The return value is the SubFont to use to display the given character.
1951 *
1952 * Side effects:
1953 *	The contents of fontPtr are modified to cache the results of the
1954 *	lookup and remember any SubFonts that were dynamically loaded. The
1955 *	table of SubFonts might be extended, and if a non-NULL reference to a
1956 *	subfont pointer is available, it is updated if it previously pointed
1957 *	into the old subfont table.
1958 *
1959 *-------------------------------------------------------------------------
1960 */
1961
1962static SubFont *
1963FindSubFontForChar(
1964    UnixFont *fontPtr,		/* The font object with which the character
1965				 * will be displayed. */
1966    int ch,			/* The Unicode character to be displayed. */
1967    SubFont **fixSubFontPtrPtr)	/* Subfont reference to fix up if we
1968				 * reallocate our subfont table. */
1969{
1970    int i, j, k, numNames;
1971    Tk_Uid faceName;
1972    char *fallback, **aliases, **nameList, **anyFallbacks, ***fontFallbacks;
1973    SubFont *subFontPtr;
1974    Tcl_DString ds;
1975
1976    if (FontMapLookup(&fontPtr->subFontArray[0], ch)) {
1977	return &fontPtr->subFontArray[0];
1978    }
1979
1980    for (i = 1; i < fontPtr->numSubFonts; i++) {
1981	if (FontMapLookup(&fontPtr->subFontArray[i], ch)) {
1982	    return &fontPtr->subFontArray[i];
1983	}
1984    }
1985
1986    if (FontMapLookup(&fontPtr->controlSubFont, ch)) {
1987	return &fontPtr->controlSubFont;
1988    }
1989
1990    /*
1991     * Keep track of all face names that we check, so we don't check some name
1992     * multiple times if it can be reached by multiple paths.
1993     */
1994
1995    Tcl_DStringInit(&ds);
1996
1997    /*
1998     * Are there any other fonts with the same face name as the base font that
1999     * could display this character, e.g., if the base font is
2000     * adobe:fixed:iso8859-1, we could might be able to use
2001     * misc:fixed:iso8859-8 or sony:fixed:jisx0208.1983-0
2002     */
2003
2004    faceName = fontPtr->font.fa.family;
2005    if (SeenName(faceName, &ds) == 0) {
2006	subFontPtr = CanUseFallback(fontPtr, faceName, ch, fixSubFontPtrPtr);
2007	if (subFontPtr != NULL) {
2008	    goto end;
2009	}
2010    }
2011
2012    aliases = TkFontGetAliasList(faceName);
2013
2014    subFontPtr = NULL;
2015    fontFallbacks = TkFontGetFallbacks();
2016    for (i = 0; fontFallbacks[i] != NULL; i++) {
2017	for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
2018	    if (strcasecmp(fallback, faceName) == 0) {
2019		/*
2020		 * If the base font has a fallback...
2021		 */
2022
2023		goto tryfallbacks;
2024	    } else if (aliases != NULL) {
2025		/*
2026		 * Or if an alias for the base font has a fallback...
2027		 */
2028
2029		for (k = 0; aliases[k] != NULL; k++) {
2030		    if (strcasecmp(fallback, aliases[k]) == 0) {
2031			goto tryfallbacks;
2032		    }
2033		}
2034	    }
2035	}
2036	continue;
2037
2038    tryfallbacks:
2039
2040	/*
2041	 * ...then see if we can use one of the fallbacks, or an alias for one
2042	 * of the fallbacks.
2043	 */
2044
2045	for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) {
2046	    subFontPtr = CanUseFallbackWithAliases(fontPtr, fallback, ch, &ds,
2047		    fixSubFontPtrPtr);
2048	    if (subFontPtr != NULL) {
2049		goto end;
2050	    }
2051	}
2052    }
2053
2054    /*
2055     * See if we can use something from the global fallback list.
2056     */
2057
2058    anyFallbacks = TkFontGetGlobalClass();
2059    for (i = 0; (fallback = anyFallbacks[i]) != NULL; i++) {
2060	subFontPtr = CanUseFallbackWithAliases(fontPtr, fallback, ch, &ds,
2061		fixSubFontPtrPtr);
2062	if (subFontPtr != NULL) {
2063	    goto end;
2064	}
2065    }
2066
2067    /*
2068     * Try all face names available in the whole system until we find one that
2069     * can be used.
2070     */
2071
2072    nameList = ListFonts(fontPtr->display, "*", &numNames);
2073    for (i = 0; i < numNames; i++) {
2074	fallback = strchr(nameList[i] + 1, '-') + 1;
2075	strchr(fallback, '-')[0] = '\0';
2076	if (SeenName(fallback, &ds) == 0) {
2077	    subFontPtr = CanUseFallback(fontPtr, fallback, ch,
2078		    fixSubFontPtrPtr);
2079	    if (subFontPtr != NULL) {
2080		XFreeFontNames(nameList);
2081		goto end;
2082	    }
2083	}
2084    }
2085    XFreeFontNames(nameList);
2086
2087  end:
2088    Tcl_DStringFree(&ds);
2089
2090    if (subFontPtr == NULL) {
2091	/*
2092	 * No font can display this character, so it will be displayed as a
2093	 * control character expansion.
2094	 */
2095
2096	subFontPtr = &fontPtr->controlSubFont;
2097	FontMapInsert(subFontPtr, ch);
2098    }
2099    return subFontPtr;
2100}
2101
2102/*
2103 *-------------------------------------------------------------------------
2104 *
2105 * FontMapLookup --
2106 *
2107 *	See if the screen font can display the given character.
2108 *
2109 * Results:
2110 *	The return value is 0 if the screen font cannot display the character,
2111 *	non-zero otherwise.
2112 *
2113 * Side effects:
2114 *	New pages are added to the font mapping cache whenever the character
2115 *	belongs to a page that hasn't been seen before. When a page is loaded,
2116 *	information about all the characters on that page is stored, not just
2117 *	for the single character in question.
2118 *
2119 *-------------------------------------------------------------------------
2120 */
2121
2122static int
2123FontMapLookup(
2124    SubFont *subFontPtr,	/* Contains font mapping cache to be queried
2125				 * and possibly updated. */
2126    int ch)			/* Character to be tested. */
2127{
2128    int row, bitOffset;
2129
2130    row = ch >> FONTMAP_SHIFT;
2131    if (subFontPtr->fontMap[row] == NULL) {
2132	FontMapLoadPage(subFontPtr, row);
2133    }
2134    bitOffset = ch & (FONTMAP_BITSPERPAGE - 1);
2135    return (subFontPtr->fontMap[row][bitOffset >> 3] >> (bitOffset & 7)) & 1;
2136}
2137
2138/*
2139 *-------------------------------------------------------------------------
2140 *
2141 * FontMapInsert --
2142 *
2143 *	Tell the font mapping cache that the given screen font should be used
2144 *	to display the specified character. This is called when no font on the
2145 *	system can be be found that can display that character; we lie to the
2146 *	font and tell it that it can display the character, otherwise we would
2147 *	end up re-searching the entire fallback hierarchy every time that
2148 *	character was seen.
2149 *
2150 * Results:
2151 *	None.
2152 *
2153 * Side effects:
2154 *	New pages are added to the font mapping cache whenever the character
2155 *	belongs to a page that hasn't been seen before. When a page is loaded,
2156 *	information about all the characters on that page is stored, not just
2157 *	for the single character in question.
2158 *
2159 *-------------------------------------------------------------------------
2160 */
2161
2162static void
2163FontMapInsert(
2164    SubFont *subFontPtr,	/* Contains font mapping cache to be
2165				 * updated. */
2166    int ch)			/* Character to be added to cache. */
2167{
2168    int row, bitOffset;
2169
2170    row = ch >> FONTMAP_SHIFT;
2171    if (subFontPtr->fontMap[row] == NULL) {
2172	FontMapLoadPage(subFontPtr, row);
2173    }
2174    bitOffset = ch & (FONTMAP_BITSPERPAGE - 1);
2175    subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7);
2176}
2177
2178/*
2179 *-------------------------------------------------------------------------
2180 *
2181 * FontMapLoadPage --
2182 *
2183 *	Load information about all the characters on a given page. This
2184 *	information consists of one bit per character that indicates whether
2185 *	the associated screen font can (1) or cannot (0) display the
2186 *	characters on the page.
2187 *
2188 * Results:
2189 *	None.
2190 *
2191 * Side effects:
2192 *	Memory allocated.
2193 *
2194 *-------------------------------------------------------------------------
2195 */
2196static void
2197FontMapLoadPage(
2198    SubFont *subFontPtr,	/* Contains font mapping cache to be
2199				 * updated. */
2200    int row)			/* Index of the page to be loaded into the
2201				 * cache. */
2202{
2203    char buf[16], src[TCL_UTF_MAX];
2204    int minHi, maxHi, minLo, maxLo, scale, checkLo;
2205    int i, end, bitOffset, isTwoByteFont, n;
2206    Tcl_Encoding encoding;
2207    XFontStruct *fontStructPtr;
2208    XCharStruct *widths;
2209    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
2210	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
2211
2212    subFontPtr->fontMap[row] = (char *) ckalloc(FONTMAP_BITSPERPAGE / 8);
2213    memset(subFontPtr->fontMap[row], 0, FONTMAP_BITSPERPAGE / 8);
2214
2215    if (subFontPtr->familyPtr == &tsdPtr->controlFamily) {
2216	return;
2217    }
2218
2219    fontStructPtr = subFontPtr->fontStructPtr;
2220    encoding = subFontPtr->familyPtr->encoding;
2221    isTwoByteFont = subFontPtr->familyPtr->isTwoByteFont;
2222
2223    widths = fontStructPtr->per_char;
2224    minHi = fontStructPtr->min_byte1;
2225    maxHi = fontStructPtr->max_byte1;
2226    minLo = fontStructPtr->min_char_or_byte2;
2227    maxLo = fontStructPtr->max_char_or_byte2;
2228    scale = maxLo - minLo + 1;
2229    checkLo = minLo;
2230
2231    if (! isTwoByteFont) {
2232	if (minLo < 32) {
2233	    checkLo = 32;
2234	}
2235    }
2236
2237    end = (row + 1) << FONTMAP_SHIFT;
2238    for (i = row << FONTMAP_SHIFT; i < end; i++) {
2239	int hi, lo;
2240
2241	if (Tcl_UtfToExternal(NULL, encoding, src, Tcl_UniCharToUtf(i, src),
2242		TCL_ENCODING_STOPONERROR, NULL, buf, sizeof(buf), NULL,
2243		NULL, NULL) != TCL_OK) {
2244	    continue;
2245	}
2246	if (isTwoByteFont) {
2247	    hi = ((unsigned char *) buf)[0];
2248	    lo = ((unsigned char *) buf)[1];
2249	} else {
2250	    hi = 0;
2251	    lo = ((unsigned char *) buf)[0];
2252	}
2253	if ((hi < minHi) || (hi > maxHi) || (lo < checkLo) || (lo > maxLo)) {
2254	    continue;
2255	}
2256	n = (hi - minHi) * scale + lo - minLo;
2257	if ((widths == NULL) || (widths[n].width + widths[n].rbearing != 0)) {
2258	    bitOffset = i & (FONTMAP_BITSPERPAGE - 1);
2259	    subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7);
2260	}
2261    }
2262}
2263
2264/*
2265 *---------------------------------------------------------------------------
2266 *
2267 * CanUseFallbackWithAliases --
2268 *
2269 *	Helper function for FindSubFontForChar. Determine if the specified
2270 *	face name (or an alias of the specified face name) can be used to
2271 *	construct a screen font that can display the given character.
2272 *
2273 * Results:
2274 *	See CanUseFallback().
2275 *
2276 * Side effects:
2277 *	If the name and/or one of its aliases was rejected, the rejected
2278 *	string is recorded in nameTriedPtr so that it won't be tried again.
2279 *	The table of SubFonts might be extended, and if a non-NULL reference
2280 *	to a subfont pointer is available, it is updated if it previously
2281 *	pointed into the old subfont table.
2282 *
2283 *---------------------------------------------------------------------------
2284 */
2285
2286static SubFont *
2287CanUseFallbackWithAliases(
2288    UnixFont *fontPtr,		/* The font object that will own the new
2289				 * screen font. */
2290    char *faceName,		/* Desired face name for new screen font. */
2291    int ch,			/* The Unicode character that the new screen
2292				 * font must be able to display. */
2293    Tcl_DString *nameTriedPtr,	/* Records face names that have already been
2294				 * tried. It is possible for the same face
2295				 * name to be queried multiple times when
2296				 * trying to find a suitable screen font. */
2297    SubFont **fixSubFontPtrPtr)	/* Subfont reference to fix up if we
2298				 * reallocate our subfont table. */
2299{
2300    SubFont *subFontPtr;
2301    char **aliases;
2302    int i;
2303
2304    if (SeenName(faceName, nameTriedPtr) == 0) {
2305	subFontPtr = CanUseFallback(fontPtr, faceName, ch, fixSubFontPtrPtr);
2306	if (subFontPtr != NULL) {
2307	    return subFontPtr;
2308	}
2309    }
2310    aliases = TkFontGetAliasList(faceName);
2311    if (aliases != NULL) {
2312	for (i = 0; aliases[i] != NULL; i++) {
2313	    if (SeenName(aliases[i], nameTriedPtr) == 0) {
2314		subFontPtr = CanUseFallback(fontPtr, aliases[i], ch,
2315			fixSubFontPtrPtr);
2316		if (subFontPtr != NULL) {
2317		    return subFontPtr;
2318		}
2319	    }
2320	}
2321    }
2322    return NULL;
2323}
2324
2325/*
2326 *---------------------------------------------------------------------------
2327 *
2328 * SeenName --
2329 *
2330 *	Used to determine we have already tried and rejected the given face
2331 *	name when looking for a screen font that can support some Unicode
2332 *	character.
2333 *
2334 * Results:
2335 *	The return value is 0 if this face name has not already been seen,
2336 *	non-zero otherwise.
2337 *
2338 * Side effects:
2339 *	None.
2340 *
2341 *---------------------------------------------------------------------------
2342 */
2343
2344static int
2345SeenName(
2346    CONST char *name,		/* The name to check. */
2347    Tcl_DString *dsPtr)		/* Contains names that have already been
2348				 * seen. */
2349{
2350    CONST char *seen, *end;
2351
2352    seen = Tcl_DStringValue(dsPtr);
2353    end = seen + Tcl_DStringLength(dsPtr);
2354    while (seen < end) {
2355	if (strcasecmp(seen, name) == 0) {
2356	    return 1;
2357	}
2358	seen += strlen(seen) + 1;
2359    }
2360    Tcl_DStringAppend(dsPtr, (char *) name, (int) (strlen(name) + 1));
2361    return 0;
2362}
2363
2364/*
2365 *-------------------------------------------------------------------------
2366 *
2367 * CanUseFallback --
2368 *
2369 *	If the specified screen font has not already been loaded into the font
2370 *	object, determine if the specified screen font can display the given
2371 *	character.
2372 *
2373 * Results:
2374 *	The return value is a pointer to a newly allocated SubFont, owned by
2375 *	the font object. This SubFont can be used to display the given
2376 *	character. The SubFont represents the screen font with the base set of
2377 *	font attributes from the font object, but using the specified face
2378 *	name. NULL is returned if the font object already holds a reference to
2379 *	the specified font or if the specified font doesn't exist or cannot
2380 *	display the given character.
2381 *
2382 * Side effects:
2383 *	The font object's subFontArray is updated to contain a reference to
2384 *	the newly allocated SubFont. The table of SubFonts might be extended,
2385 *	and if a non-NULL reference to a subfont pointer is available, it is
2386 *	updated if it previously pointed into the old subfont table.
2387 *
2388 *-------------------------------------------------------------------------
2389 */
2390
2391static SubFont *
2392CanUseFallback(
2393    UnixFont *fontPtr,		/* The font object that will own the new
2394				 * screen font. */
2395    CONST char *faceName,	/* Desired face name for new screen font. */
2396    int ch,			/* The Unicode character that the new screen
2397				 * font must be able to display. */
2398    SubFont **fixSubFontPtrPtr)	/* Subfont reference to fix up if we
2399				 * reallocate our subfont table. */
2400{
2401    int i, nameIdx, numNames, srcLen, numEncodings, bestIdx[2];
2402    Tk_Uid hateFoundry;
2403    CONST char *charset, *hateCharset;
2404    unsigned int bestScore[2];
2405    char **nameList, **nameListOrig, src[TCL_UTF_MAX];
2406    FontAttributes want, got;
2407    Display *display;
2408    SubFont subFont;
2409    XFontStruct *fontStructPtr;
2410    Tcl_DString dsEncodings;
2411    Tcl_Encoding *encodingCachePtr;
2412
2413    /*
2414     * Assume: the face name is times.
2415     * Assume: adobe:times:iso8859-1 has already been used.
2416     *
2417     * Are there any versions of times that can display this character (e.g.,
2418     *    perhaps linotype:times:iso8859-2)?
2419     *	  a. Get list of all times fonts.
2420     *	  b1. Cross out all names whose encodings we've already used.
2421     *	  b2. Cross out all names whose foundry & encoding we've already seen.
2422     *	  c. Cross out all names whose encoding cannot handle the character.
2423     *	  d. Rank each name and pick the best match.
2424     *	  e. If that font cannot actually display the character, cross out all
2425     *	     names with the same foundry and encoding and go back to (c).
2426     */
2427
2428    display = fontPtr->display;
2429    nameList = ListFonts(display, faceName, &numNames);
2430    if (numNames == 0) {
2431	return NULL;
2432    }
2433    nameListOrig = nameList;
2434
2435    srcLen = Tcl_UniCharToUtf(ch, src);
2436
2437    want.fa = fontPtr->font.fa;
2438    want.xa = fontPtr->xa;
2439
2440    want.fa.family = Tk_GetUid(faceName);
2441    want.fa.size = -fontPtr->pixelSize;
2442
2443    hateFoundry = NULL;
2444    hateCharset = NULL;
2445    numEncodings = 0;
2446    Tcl_DStringInit(&dsEncodings);
2447
2448    charset = NULL;	/* lint, since numNames must be > 0 to get here. */
2449
2450  retry:
2451    bestIdx[0] = -1;
2452    bestIdx[1] = -1;
2453    bestScore[0] = (unsigned int) -1;
2454    bestScore[1] = (unsigned int) -1;
2455    for (nameIdx = 0; nameIdx < numNames; nameIdx++) {
2456	Tcl_Encoding encoding;
2457	char dst[16];
2458	int scalable, srcRead, dstWrote;
2459	unsigned int score;
2460
2461	if (nameList[nameIdx] == NULL) {
2462	    continue;
2463	}
2464	if (TkFontParseXLFD(nameList[nameIdx], &got.fa, &got.xa) != TCL_OK) {
2465	    goto crossout;
2466	}
2467	IdentifySymbolEncodings(&got);
2468	charset = GetEncodingAlias(got.xa.charset);
2469	if (hateFoundry != NULL) {
2470	    /*
2471	     * E. If the font we picked cannot actually display the character,
2472	     * cross out all names with the same foundry and encoding.
2473	     */
2474
2475	    if ((hateFoundry == got.xa.foundry)
2476		    && (strcmp(hateCharset, charset) == 0)) {
2477		goto crossout;
2478	    }
2479	} else {
2480	    /*
2481	     * B. Cross out all names whose encodings we've already used.
2482	     */
2483
2484	    for (i = 0; i < fontPtr->numSubFonts; i++) {
2485		encoding = fontPtr->subFontArray[i].familyPtr->encoding;
2486		if (strcmp(charset, Tcl_GetEncodingName(encoding)) == 0) {
2487		    goto crossout;
2488		}
2489	    }
2490	}
2491
2492	/*
2493	 * C. Cross out all names whose encoding cannot handle the character.
2494	 */
2495
2496	encodingCachePtr = (Tcl_Encoding *) Tcl_DStringValue(&dsEncodings);
2497	for (i = numEncodings; --i >= 0; encodingCachePtr++) {
2498	    encoding = *encodingCachePtr;
2499	    if (strcmp(Tcl_GetEncodingName(encoding), charset) == 0) {
2500		break;
2501	    }
2502	}
2503	if (i < 0) {
2504	    encoding = Tcl_GetEncoding(NULL, charset);
2505	    if (encoding == NULL) {
2506		goto crossout;
2507	    }
2508
2509	    Tcl_DStringAppend(&dsEncodings, (char *) &encoding,
2510		    sizeof(encoding));
2511	    numEncodings++;
2512	}
2513	Tcl_UtfToExternal(NULL, encoding, src, srcLen,
2514		TCL_ENCODING_STOPONERROR, NULL, dst, sizeof(dst), &srcRead,
2515		&dstWrote, NULL);
2516	if (dstWrote == 0) {
2517	    goto crossout;
2518	}
2519
2520	/*
2521	 * D. Rank each name and pick the best match.
2522	 */
2523
2524	scalable = (got.fa.size == 0);
2525	score = RankAttributes(&want, &got);
2526	if (score < bestScore[scalable]) {
2527	    bestIdx[scalable] = nameIdx;
2528	    bestScore[scalable] = score;
2529	}
2530	if (score == 0) {
2531	    break;
2532	}
2533	continue;
2534
2535    crossout:
2536	if (nameList == nameListOrig) {
2537	    /*
2538	     * Not allowed to change pointers to memory that X gives you, so
2539	     * make a copy.
2540	     */
2541
2542	    nameList = (char **) ckalloc(numNames * sizeof(char *));
2543	    memcpy(nameList, nameListOrig, numNames * sizeof(char *));
2544	}
2545	nameList[nameIdx] = NULL;
2546    }
2547
2548    fontStructPtr = GetScreenFont(display, &want, nameList, bestIdx,
2549	    bestScore);
2550
2551    encodingCachePtr = (Tcl_Encoding *) Tcl_DStringValue(&dsEncodings);
2552    for (i = numEncodings; --i >= 0; encodingCachePtr++) {
2553	Tcl_FreeEncoding(*encodingCachePtr);
2554    }
2555    Tcl_DStringFree(&dsEncodings);
2556    numEncodings = 0;
2557
2558    if (fontStructPtr == NULL) {
2559	if (nameList != nameListOrig) {
2560	    ckfree((char *) nameList);
2561	}
2562	XFreeFontNames(nameListOrig);
2563	return NULL;
2564    }
2565
2566    InitSubFont(display, fontStructPtr, 0, &subFont);
2567    if (FontMapLookup(&subFont, ch) == 0) {
2568	/*
2569	 * E. If the font we picked cannot actually display the character,
2570	 * cross out all names with the same foundry and encoding and pick
2571	 * another font.
2572	 */
2573
2574	hateFoundry = got.xa.foundry;
2575	hateCharset = charset;
2576	ReleaseSubFont(display, &subFont);
2577	goto retry;
2578    }
2579    if (nameList != nameListOrig) {
2580	ckfree((char *) nameList);
2581    }
2582    XFreeFontNames(nameListOrig);
2583
2584    if (fontPtr->numSubFonts >= SUBFONT_SPACE) {
2585	SubFont *newPtr;
2586
2587	newPtr = (SubFont *)
2588		ckalloc(sizeof(SubFont) * (fontPtr->numSubFonts + 1));
2589	memcpy((char *) newPtr, fontPtr->subFontArray,
2590		fontPtr->numSubFonts * sizeof(SubFont));
2591	if (fixSubFontPtrPtr != NULL) {
2592	    register SubFont *fixSubFontPtr = *fixSubFontPtrPtr;
2593
2594	    if (fixSubFontPtr != &fontPtr->controlSubFont) {
2595		*fixSubFontPtrPtr =
2596			newPtr + (fixSubFontPtr - fontPtr->subFontArray);
2597	    }
2598	}
2599	if (fontPtr->subFontArray != fontPtr->staticSubFonts) {
2600	    ckfree((char *) fontPtr->subFontArray);
2601	}
2602	fontPtr->subFontArray = newPtr;
2603    }
2604    fontPtr->subFontArray[fontPtr->numSubFonts] = subFont;
2605    fontPtr->numSubFonts++;
2606    return &fontPtr->subFontArray[fontPtr->numSubFonts - 1];
2607}
2608
2609/*
2610 *---------------------------------------------------------------------------
2611 *
2612 * RankAttributes --
2613 *
2614 *	Determine how close the attributes of the font in question match the
2615 *	attributes that we want.
2616 *
2617 * Results:
2618 *	The return value is the score; lower numbers are better. *scalablePtr
2619 *	is set to 0 if the font was not scalable, 1 otherwise.
2620 *
2621 * Side effects:
2622 *	None.
2623 *
2624 *---------------------------------------------------------------------------
2625 */
2626
2627static unsigned int
2628RankAttributes(
2629    FontAttributes *wantPtr,	/* The desired attributes. */
2630    FontAttributes *gotPtr)	/* The attributes we have to live with. */
2631{
2632    unsigned int penalty;
2633
2634    penalty = 0;
2635    if (gotPtr->xa.foundry != wantPtr->xa.foundry) {
2636	penalty += 4500;
2637    }
2638    if (gotPtr->fa.family != wantPtr->fa.family) {
2639	penalty += 9000;
2640    }
2641    if (gotPtr->fa.weight != wantPtr->fa.weight) {
2642	penalty += 90;
2643    }
2644    if (gotPtr->fa.slant != wantPtr->fa.slant) {
2645	penalty += 60;
2646    }
2647    if (gotPtr->xa.slant != wantPtr->xa.slant) {
2648	penalty += 10;
2649    }
2650    if (gotPtr->xa.setwidth != wantPtr->xa.setwidth) {
2651	penalty += 1000;
2652    }
2653
2654    if (gotPtr->fa.size == 0) {
2655	/*
2656	 * A scalable font is almost always acceptable, but the corresponding
2657	 * bitmapped font would be better.
2658	 */
2659
2660	penalty += 10;
2661    } else {
2662	int diff;
2663
2664	/*
2665	 * It's worse to be too large than to be too small.
2666	 */
2667
2668	diff = (-gotPtr->fa.size - -wantPtr->fa.size);
2669	if (diff > 0) {
2670	    penalty += 600;
2671	} else if (diff < 0) {
2672	    penalty += 150;
2673	    diff = -diff;
2674	}
2675	penalty += 150 * diff;
2676    }
2677    if (gotPtr->xa.charset != wantPtr->xa.charset) {
2678	int i;
2679	CONST char *gotAlias, *wantAlias;
2680
2681	penalty += 65000;
2682	gotAlias = GetEncodingAlias(gotPtr->xa.charset);
2683	wantAlias = GetEncodingAlias(wantPtr->xa.charset);
2684	if (strcmp(gotAlias, wantAlias) != 0) {
2685	    penalty += 30000;
2686	    for (i = 0; encodingList[i] != NULL; i++) {
2687		if (strcmp(gotAlias, encodingList[i]) == 0) {
2688		    penalty -= 30000;
2689		    break;
2690		}
2691		penalty += 20000;
2692	    }
2693	}
2694    }
2695    return penalty;
2696}
2697
2698/*
2699 *---------------------------------------------------------------------------
2700 *
2701 * GetScreenFont --
2702 *
2703 *	Given the names for the best scalable and best bitmapped font,
2704 *	actually construct an XFontStruct based on the best XLFD. This is
2705 *	where all the alias and fallback substitution bottoms out.
2706 *
2707 * Results:
2708 *	The screen font that best corresponds to the set of attributes.
2709 *
2710 * Side effects:
2711 *	None.
2712 *
2713 *---------------------------------------------------------------------------
2714 */
2715
2716static XFontStruct *
2717GetScreenFont(
2718    Display *display,		/* Display for new XFontStruct. */
2719    FontAttributes *wantPtr,	/* Contains desired actual pixel-size if the
2720				 * best font was scalable. */
2721    char **nameList,		/* Array of XLFDs. */
2722    int bestIdx[2],		/* Indices into above array for XLFD of best
2723				 * bitmapped and best scalable font. */
2724    unsigned int bestScore[2])	/* Scores of best bitmapped and best scalable
2725				 * font. XLFD corresponding to lowest score
2726				 * will be constructed. */
2727{
2728    XFontStruct *fontStructPtr;
2729
2730    if ((bestIdx[0] < 0) && (bestIdx[1] < 0)) {
2731	return NULL;
2732    }
2733
2734    /*
2735     * Now we know which is the closest matching scalable font and the closest
2736     * matching bitmapped font. If the scalable font was a better match, try
2737     * getting the scalable font; however, if the scalable font was not
2738     * actually available in the desired pointsize, fall back to the closest
2739     * bitmapped font.
2740     */
2741
2742    fontStructPtr = NULL;
2743    if (bestScore[1] < bestScore[0]) {
2744	char *str, *rest, buf[256];
2745	int i;
2746
2747	/*
2748	 * Fill in the desired pixel size for this font.
2749	 */
2750
2751    tryscale:
2752	str = nameList[bestIdx[1]];
2753	for (i = 0; i < XLFD_PIXEL_SIZE; i++) {
2754	    str = strchr(str + 1, '-');
2755	}
2756	rest = str;
2757	for (i = XLFD_PIXEL_SIZE; i < XLFD_CHARSET; i++) {
2758	    rest = strchr(rest + 1, '-');
2759	}
2760	*str = '\0';
2761	sprintf(buf, "%.200s-%d-*-*-*-*-*%s", nameList[bestIdx[1]],
2762		-wantPtr->fa.size, rest);
2763	*str = '-';
2764	fontStructPtr = XLoadQueryFont(display, buf);
2765	bestScore[1] = INT_MAX;
2766    }
2767    if (fontStructPtr == NULL) {
2768	fontStructPtr = XLoadQueryFont(display, nameList[bestIdx[0]]);
2769	if (fontStructPtr == NULL) {
2770	    /*
2771	     * This shouldn't happen because the font name is one of the names
2772	     * that X gave us to use, but it does anyhow.
2773	     */
2774
2775	    if (bestScore[1] < INT_MAX) {
2776		goto tryscale;
2777	    }
2778	    return GetSystemFont(display);
2779	}
2780    }
2781    return fontStructPtr;
2782}
2783
2784/*
2785 *---------------------------------------------------------------------------
2786 *
2787 * GetSystemFont --
2788 *
2789 *	Absolute fallback mechanism, called when we need a font and no other
2790 *	font can be found and/or instantiated.
2791 *
2792 * Results:
2793 *	A pointer to a font. Never NULL.
2794 *
2795 * Side effects:
2796 *	If there are NO fonts installed on the system, this call will panic,
2797 *	but how did you get X running in that case?
2798 *
2799 *---------------------------------------------------------------------------
2800 */
2801
2802static XFontStruct *
2803GetSystemFont(
2804    Display *display)		/* Display for new XFontStruct. */
2805{
2806    XFontStruct *fontStructPtr;
2807
2808    fontStructPtr = XLoadQueryFont(display, "fixed");
2809    if (fontStructPtr == NULL) {
2810	fontStructPtr = XLoadQueryFont(display, "*");
2811	if (fontStructPtr == NULL) {
2812	    Tcl_Panic("TkpGetFontFromAttributes: cannot get any font");
2813	}
2814    }
2815    return fontStructPtr;
2816}
2817
2818/*
2819 *---------------------------------------------------------------------------
2820 *
2821 * GetFontAttributes --
2822 *
2823 *	Given a screen font, determine its actual attributes, which are not
2824 *	necessarily the attributes that were used to construct it.
2825 *
2826 * Results:
2827 *	*faPtr is filled with the screen font's attributes.
2828 *
2829 * Side effects:
2830 *	None.
2831 *
2832 *---------------------------------------------------------------------------
2833 */
2834
2835static int
2836GetFontAttributes(
2837    Display *display,		/* Display that owns the screen font. */
2838    XFontStruct *fontStructPtr,	/* Screen font to query. */
2839    FontAttributes *faPtr)	/* For storing attributes of screen font. */
2840{
2841    unsigned long value;
2842    char *name;
2843
2844    if ((XGetFontProperty(fontStructPtr, XA_FONT, &value) != False) &&
2845	    (value != 0)) {
2846	name = XGetAtomName(display, (Atom) value);
2847	if (TkFontParseXLFD(name, &faPtr->fa, &faPtr->xa) != TCL_OK) {
2848	    faPtr->fa.family = Tk_GetUid(name);
2849	    faPtr->xa.foundry = Tk_GetUid("");
2850	    faPtr->xa.charset = Tk_GetUid("");
2851	}
2852	XFree(name);
2853    } else {
2854	TkInitFontAttributes(&faPtr->fa);
2855	TkInitXLFDAttributes(&faPtr->xa);
2856    }
2857
2858    /*
2859     * Do last ditch check for family. It seems that some X servers can fail
2860     * on the X font calls above, slipping through earlier checks. X-Win32 5.4
2861     * is one of these.
2862     */
2863
2864    if (faPtr->fa.family == NULL) {
2865	faPtr->fa.family = Tk_GetUid("");
2866	faPtr->xa.foundry = Tk_GetUid("");
2867	faPtr->xa.charset = Tk_GetUid("");
2868    }
2869    return IdentifySymbolEncodings(faPtr);
2870}
2871
2872/*
2873 *---------------------------------------------------------------------------
2874 *
2875 * ListFonts --
2876 *
2877 *	Utility function to return the array of all XLFDs on the system with
2878 *	the specified face name.
2879 *
2880 * Results:
2881 *	The return value is an array of XLFDs, which should be freed with
2882 *	XFreeFontNames(), or NULL if no XLFDs matched the requested name.
2883 *
2884 * Side effects:
2885 *	None.
2886 *
2887 *---------------------------------------------------------------------------
2888 */
2889
2890static char **
2891ListFonts(
2892    Display *display,		/* Display to query. */
2893    CONST char *faceName,	/* Desired face name, or "*" for all. */
2894    int *numNamesPtr)		/* Filled with length of returned array, or 0
2895				 * if no names were found. */
2896{
2897    char buf[256];
2898
2899    sprintf(buf, "-*-%.80s-*-*-*-*-*-*-*-*-*-*-*-*", faceName);
2900    return XListFonts(display, buf, 10000, numNamesPtr);
2901}
2902
2903static char **
2904ListFontOrAlias(
2905    Display *display,		/* Display to query. */
2906    CONST char *faceName,	/* Desired face name, or "*" for all. */
2907    int *numNamesPtr)		/* Filled with length of returned array, or 0
2908				 * if no names were found. */
2909{
2910    char **nameList, **aliases;
2911    int i;
2912
2913    nameList = ListFonts(display, faceName, numNamesPtr);
2914    if (nameList != NULL) {
2915	return nameList;
2916    }
2917    aliases = TkFontGetAliasList(faceName);
2918    if (aliases != NULL) {
2919	for (i = 0; aliases[i] != NULL; i++) {
2920	    nameList = ListFonts(display, aliases[i], numNamesPtr);
2921	    if (nameList != NULL) {
2922		return nameList;
2923	    }
2924	}
2925    }
2926    *numNamesPtr = 0;
2927    return NULL;
2928}
2929
2930/*
2931 *---------------------------------------------------------------------------
2932 *
2933 * IdentifySymbolEncodings --
2934 *
2935 *	If the font attributes refer to a symbol font, update the charset
2936 *	field of the font attributes so that it reflects the encoding of that
2937 *	symbol font. In general, the raw value for the charset field parsed
2938 *	from an XLFD is meaningless for symbol fonts.
2939 *
2940 *	Symbol fonts are all fonts whose name appears in the symbolClass.
2941 *
2942 * Results:
2943 *	The return value is non-zero if the font attributes specify a symbol
2944 *	font, or 0 otherwise. If a non-zero value is returned the charset
2945 *	field of the font attributes will be changed to the string that
2946 *	represents the actual encoding for the symbol font.
2947 *
2948 * Side effects:
2949 *	None.
2950 *
2951 *---------------------------------------------------------------------------
2952 */
2953
2954static int
2955IdentifySymbolEncodings(
2956    FontAttributes *faPtr)
2957{
2958    int i, j;
2959    char **aliases, **symbolClass;
2960
2961    symbolClass = TkFontGetSymbolClass();
2962    for (i = 0; symbolClass[i] != NULL; i++) {
2963	if (strcasecmp(faPtr->fa.family, symbolClass[i]) == 0) {
2964	    faPtr->xa.charset = Tk_GetUid(GetEncodingAlias(symbolClass[i]));
2965	    return 1;
2966	}
2967	aliases = TkFontGetAliasList(symbolClass[i]);
2968	for (j = 0; (aliases != NULL) && (aliases[j] != NULL); j++) {
2969	    if (strcasecmp(faPtr->fa.family, aliases[j]) == 0) {
2970		faPtr->xa.charset = Tk_GetUid(GetEncodingAlias(aliases[j]));
2971		return 1;
2972	    }
2973	}
2974    }
2975    return 0;
2976}
2977
2978/*
2979 *---------------------------------------------------------------------------
2980 *
2981 * GetEncodingAlias --
2982 *
2983 *	Map the name of an encoding to another name that should be used when
2984 *	actually loading the encoding. For instance, the encodings
2985 *	"jisc6226.1978", "jisx0208.1983", "jisx0208.1990", and "jisx0208.1996"
2986 *	are well-known names for the same encoding and are represented by one
2987 *	encoding table: "jis0208".
2988 *
2989 * Results:
2990 *	As above. If the name has no alias, the original name is returned.
2991 *
2992 * Side effects:
2993 *	None.
2994 *
2995 *---------------------------------------------------------------------------
2996 */
2997
2998static CONST char *
2999GetEncodingAlias(
3000    CONST char *name)		/* The name to look up. */
3001{
3002    EncodingAlias *aliasPtr;
3003
3004    for (aliasPtr = encodingAliases; aliasPtr->aliasPattern != NULL; ) {
3005	if (Tcl_StringMatch((char *) name, aliasPtr->aliasPattern)) {
3006	    return aliasPtr->realName;
3007	}
3008	aliasPtr++;
3009    }
3010    return name;
3011}
3012
3013/*
3014 * Local Variables:
3015 * mode: c
3016 * c-basic-offset: 4
3017 * fill-column: 78
3018 * End:
3019 */
3020