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