Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkFont.c

/* 
 * tkFont.c --
 *
 *    This file maintains a database of fonts for the Tk toolkit.
 *    It also provides several utility procedures for measuring and
 *    displaying text.
 *
 * Copyright (c) 1990-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkFont.c,v 1.2 1998/09/14 18:23:10 stanton Exp $
 */

#include "tkInt.h"
#include "tkFont.h"

/*
 * The following structure is used to keep track of all the fonts that
 * exist in the current application.  It must be stored in the
 * TkMainInfo for the application.
 */
 
typedef struct TkFontInfo {
    Tcl_HashTable fontCache;  /* Map a string to an existing Tk_Font.
                         * Keys are CachedFontKey structs, values are
                         * TkFont structs. */
    Tcl_HashTable namedTable; /* Map a name to a set of attributes for a
                         * font, used when constructing a Tk_Font from
                         * a named font description.  Keys are
                         * Tk_Uids, values are NamedFont structs. */
    TkMainInfo *mainPtr;      /* Application that owns this structure. */
    int updatePending;        
} TkFontInfo;

/*
 * The following structure is used as a key in the fontCache.
 */

typedef struct CachedFontKey {
    Display *display;         /* Display for which font was constructed. */
    Tk_Uid string;            /* String that describes font. */
} CachedFontKey;

/*
 * The following data structure is used to keep track of the font attributes
 * for each named font that has been defined.  The named font is only deleted
 * when the last reference to it goes away.
 */

typedef struct NamedFont {
    int refCount;       /* Number of users of named font. */
    int deletePending;        /* Non-zero if font should be deleted when
                         * last reference goes away. */
    TkFontAttributes fa;      /* Desired attributes for named font. */
} NamedFont;
    
/*
 * The following two structures are used to keep track of string
 * measurement information when using the text layout facilities.
 *
 * A LayoutChunk represents a contiguous range of text that can be measured
 * and displayed by low-level text calls.  In general, chunks will be
 * delimited by newlines and tabs.  Low-level, platform-specific things
 * like kerning and non-integer character widths may occur between the
 * characters in a single chunk, but not between characters in different
 * chunks.
 *
 * A TextLayout is a collection of LayoutChunks.  It can be displayed with
 * respect to any origin.  It is the implementation of the Tk_TextLayout
 * opaque token.
 */

typedef struct LayoutChunk {
    CONST char *start;        /* Pointer to simple string to be displayed.
                         * This is a pointer into the TkTextLayout's
                         * string. */
    int numChars;       /* The number of characters in this chunk. */
    int numDisplayChars;      /* The number of characters to display when
                         * this chunk is displayed.  Can be less than
                         * numChars if extra space characters were
                         * absorbed by the end of the chunk.  This
                         * will be < 0 if this is a chunk that is
                         * holding a tab or newline. */
    int x, y;                 /* The origin of the first character in this
                         * chunk with respect to the upper-left hand
                         * corner of the TextLayout. */
    int totalWidth;           /* Width in pixels of this chunk.  Used
                         * when hit testing the invisible spaces at
                         * the end of a chunk. */
    int displayWidth;         /* Width in pixels of the displayable
                         * characters in this chunk.  Can be less than
                         * width if extra space characters were
                         * absorbed by the end of the chunk. */
} LayoutChunk;

typedef struct TextLayout {
    Tk_Font tkfont;           /* The font used when laying out the text. */
    CONST char *string;       /* The string that was layed out. */
    int width;                /* The maximum width of all lines in the
                         * text layout. */
    int numChunks;            /* Number of chunks actually used in
                         * following array. */
    LayoutChunk chunks[1];    /* Array of chunks.  The actual size will
                         * be maxChunks.  THIS FIELD MUST BE THE LAST
                         * IN THE STRUCTURE. */
} TextLayout;

/*
 * The following structures are used as two-way maps between the values for
 * the fields in the TkFontAttributes structure and the strings used in
 * Tcl, when parsing both option-value format and style-list format font
 * name strings.
 */

static TkStateMap weightMap[] = {
    {TK_FW_NORMAL,      "normal"},
    {TK_FW_BOLD,  "bold"},
    {TK_FW_UNKNOWN,     NULL}
};

static TkStateMap slantMap[] = {
    {TK_FS_ROMAN, "roman"},
    {TK_FS_ITALIC,      "italic"},
    {TK_FS_UNKNOWN,     NULL}
};

static TkStateMap underlineMap[] = {
    {1,                 "underline"},
    {0,                 NULL}
};

static TkStateMap overstrikeMap[] = {
    {1,                 "overstrike"},
    {0,                 NULL}
};

/*
 * The following structures are used when parsing XLFD's into a set of
 * TkFontAttributes.
 */

static TkStateMap xlfdWeightMap[] = {
    {TK_FW_NORMAL,      "normal"},
    {TK_FW_NORMAL,      "medium"},
    {TK_FW_NORMAL,      "book"},
    {TK_FW_NORMAL,      "light"},
    {TK_FW_BOLD,  "bold"},
    {TK_FW_BOLD,  "demi"},
    {TK_FW_BOLD,  "demibold"},
#ifdef KANJI
    {TK_FW_BOLD,  "black"},
    {TK_FW_NORMAL,      "regular"},
#endif /* KANJI */
    {TK_FW_NORMAL,      NULL}       /* Assume anything else is "normal". */
}; 

static TkStateMap xlfdSlantMap[] = {
    {TK_FS_ROMAN, "r"},
    {TK_FS_ITALIC,      "i"},
    {TK_FS_OBLIQUE,     "o"},
    {TK_FS_ROMAN, NULL}       /* Assume anything else is "roman". */
};

static TkStateMap xlfdSetwidthMap[] = {
    {TK_SW_NORMAL,      "normal"},
    {TK_SW_CONDENSE,    "narrow"},
    {TK_SW_CONDENSE,    "semicondensed"},
    {TK_SW_CONDENSE,    "condensed"},
    {TK_SW_UNKNOWN,     NULL}
};

static TkStateMap xlfdCharsetMap[] = {
    {TK_CS_NORMAL,      "iso8859"},
    {TK_CS_SYMBOL,      "adobe"},
    {TK_CS_SYMBOL,      "sun"},
#ifdef KANJI
    {TK_CS_KANJI, "jisx0208.1983"},
#endif /* KANJI */
    {TK_CS_OTHER, NULL}
};
    
/*
 * The following structure and defines specify the valid builtin options 
 * when configuring a set of font attributes.
 */

static char *fontOpt[] = {
#ifdef KANJI
    "-foundry",
#endif /* KANJI */
    "-family",
    "-size",
    "-weight",
    "-slant",
    "-underline",
    "-overstrike",
#ifdef KANJI
    "-charset",
    "-pointadjust",
    "-compound",
#endif /* KANJI */
    NULL
};

#ifdef KANJI
#define FONT_FOUNDRY    0
#define FONT_FAMILY     1
#define FONT_SIZE 2
#define FONT_WEIGHT     3
#define FONT_SLANT      4
#define FONT_UNDERLINE  5
#define FONT_OVERSTRIKE 6
#define FONT_CHARSET    7
#define FONT_POINTADJUST      8
#define FONT_COMPOUND   9
#define FONT_NUMFIELDS  10        /* Length of fontOpt array. */
#else
#define FONT_FAMILY     0
#define FONT_SIZE 1
#define FONT_WEIGHT     2
#define FONT_SLANT      3
#define FONT_UNDERLINE  4
#define FONT_OVERSTRIKE 5
#define FONT_NUMFIELDS  6         /* Length of fontOpt array. */
#endif /* KANJI */

#define GetFontAttributes(tkfont) \
            ((CONST TkFontAttributes *) &((TkFont *) (tkfont))->fa)

#define GetFontMetrics(tkfont)    \
            ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm)


static int        ConfigAttributesObj _ANSI_ARGS_((Tcl_Interp *interp,
                      Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[],
                      TkFontAttributes *faPtr));
#ifdef KANJI
static int        ConfigCompoundAttributesObj _ANSI_ARGS_((Tcl_Interp *interp,
                      Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[],
                      TkFontAttributes *faPtr));
#endif KANJI
static int        FieldSpecified _ANSI_ARGS_((CONST char *field));
static int        GetAttributeInfoObj _ANSI_ARGS_((Tcl_Interp *interp,
                      CONST TkFontAttributes *faPtr, Tcl_Obj *objPtr));
static LayoutChunk *    NewChunk _ANSI_ARGS_((TextLayout **layoutPtrPtr,
                      int *maxPtr, CONST char *start, int numChars,
                      int curX, int newX, int y));
static int        ParseFontNameObj _ANSI_ARGS_((Tcl_Interp *interp,
                      Tk_Window tkwin, Tcl_Obj *objPtr,
                      TkFontAttributes *faPtr));
static void       RecomputeWidgets _ANSI_ARGS_((TkWindow *winPtr));
static void       TheWorldHasChanged _ANSI_ARGS_((
                      ClientData clientData));
static void       UpdateDependantFonts _ANSI_ARGS_((TkFontInfo *fiPtr,
                      Tk_Window tkwin, Tcl_HashEntry *namedHashPtr));


/*
 *---------------------------------------------------------------------------
 *
 * TkFontPkgInit --
 *
 *    This procedure is called when an application is created.  It
 *    initializes all the structures that are used by the font
 *    package on a per application basis.
 *
 * Results:
 *    Returns a token that must be stored in the TkMainInfo for this
 *    application.
 *
 * Side effects:
 *    Memory allocated.
 *
 *---------------------------------------------------------------------------
 */
void
TkFontPkgInit(mainPtr)
    TkMainInfo *mainPtr;      /* The application being created. */
{
    TkFontInfo *fiPtr;

    fiPtr = (TkFontInfo *) ckalloc(sizeof(TkFontInfo));
    Tcl_InitHashTable(&fiPtr->fontCache, sizeof(CachedFontKey) / sizeof(int));
    Tcl_InitHashTable(&fiPtr->namedTable, TCL_ONE_WORD_KEYS);
    fiPtr->mainPtr = mainPtr;
    fiPtr->updatePending = 0;
    mainPtr->fontInfoPtr = fiPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkFontPkgFree --
 *
 *    This procedure is called when an application is deleted.  It
 *    deletes all the structures that were used by the font package
 *    for this application.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Memory freed.
 *
 *---------------------------------------------------------------------------
 */

void
TkFontPkgFree(mainPtr)
    TkMainInfo *mainPtr;      /* The application being deleted. */
{
    TkFontInfo *fiPtr;
    Tcl_HashEntry *hPtr;
    Tcl_HashSearch search;

    fiPtr = mainPtr->fontInfoPtr;

    if (fiPtr->fontCache.numEntries != 0) {
#ifdef KANJI
      TkFont *tkfont;
      hPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
      while (hPtr != NULL) {
          tkfont = (TkFont *)Tcl_GetHashValue(hPtr);
          if (Tk_FontType(tkfont) != TK_FONT_COMPOUND) {
            TkpDeleteFont(tkfont);
          } else {
            panic("TkFontPkgFree: must not be named font.");
          }
          hPtr = Tcl_NextHashEntry(&search);
      }
#else
      panic("TkFontPkgFree: all fonts should have been freed already");
#endif /* KANJI */
    }
    Tcl_DeleteHashTable(&fiPtr->fontCache);

    hPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
    while (hPtr != NULL) {
      ckfree((char *) Tcl_GetHashValue(hPtr));
      hPtr = Tcl_NextHashEntry(&search);
    }
    Tcl_DeleteHashTable(&fiPtr->namedTable);
    if (fiPtr->updatePending != 0) {
      Tcl_CancelIdleCall(TheWorldHasChanged, (ClientData) fiPtr);
    }
    ckfree((char *) fiPtr);
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FontObjCmd -- 
 *
 *    This procedure is implemented to process the "font" Tcl command.
 *    See the user documentation for details on what it does.
 *
 * Results:
 *    A standard Tcl result.
 *
 * Side effects:
 *    See the user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tk_FontObjCmd(clientData, interp, objc, objv)
    ClientData clientData;    /* Main window associated with interpreter. */
    Tcl_Interp *interp;       /* Current interpreter. */
    int objc;                 /* Number of arguments. */
    Tcl_Obj *CONST objv[];    /* Argument objects. */
{
    int index;
    Tk_Window tkwin;
    TkFontInfo *fiPtr;
#ifdef KANJI
    static char *optionStrings[] = {
      "actual",   "cache",    "configure",      "create",
      "delete",   "failsafe", "families", "measure",
      "metrics",  "names",    NULL
    };
    enum options {
      FONT_ACTUAL,      FONT_CACHE, FONT_CONFIGURE,   FONT_CREATE,
      FONT_DELETE,      FONT_FAILSAFE,    FONT_FAMILIES,    FONT_MEASURE,
      FONT_METRICS,     FONT_NAMES
    };
#else
    static char *optionStrings[] = {
      "actual",   "configure",      "create",   "delete",
      "families", "measure",  "metrics",  "names",
      NULL
    };
    enum options {
      FONT_ACTUAL,      FONT_CONFIGURE,   FONT_CREATE,      FONT_DELETE,
      FONT_FAMILIES,    FONT_MEASURE,     FONT_METRICS,     FONT_NAMES
    };
#endif /* KANJI */

    tkwin = (Tk_Window) clientData;
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;

    if (objc < 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
      return TCL_ERROR;
    }
    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
          &index) != TCL_OK) {
      return TCL_ERROR;
    }

    switch ((enum options) index) {
      case FONT_ACTUAL: {
          int skip, result;
          Tk_Font tkfont;
          Tcl_Obj *objPtr;
          CONST TkFontAttributes *faPtr;

          skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
          if (skip < 0) {
            return TCL_ERROR;
          }
          if ((objc < 3) || (objc - skip > 4)) {
            Tcl_WrongNumArgs(interp, 2, objv,
                  "font ?-displayof window? ?option?");
            return TCL_ERROR;
          }
          tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
          if (tkfont == NULL) {
            return TCL_ERROR;
          }
          objc -= skip;
          objv += skip;
          faPtr = GetFontAttributes(tkfont);
          objPtr = NULL;
          if (objc > 3) {
            objPtr = objv[3];
          }
          result = GetAttributeInfoObj(interp, faPtr, objPtr);
          Tk_FreeFont(tkfont);
          return result;
      }
#ifdef KANJI
        case FONT_CACHE: {
          Tk_Window dpyTkWin = tkwin;
          int skip;
          char *cmd = NULL;
          int doClear = 0;

          if (objc > 5) {
            Tcl_WrongNumArgs(interp, 2, objv,
                  "?-displayof window? ?clear?");
            return TCL_ERROR;
          }
          skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &dpyTkWin);
          if (skip < 0) {
            return TCL_ERROR;
          } else if (skip == 0) {
            skip = objc - 1;
          } else {
            skip = objc - 2 + skip - 1;
          }
          if (dpyTkWin == tkwin) {
            dpyTkWin = NULL;
          }

          if (skip > 1 && skip < objc) {
            cmd = Tcl_GetStringFromObj(objv[skip], NULL);
            if (cmd != NULL) {
                if (strcmp(cmd, "clear") == 0) {
                  doClear = 1;
                } else if (strcmp(cmd, "dump") == 0) {
                  doClear = -1;
                }
            }
          }
#ifdef __WIN32__ /* do nothing */
          return TCL_OK;
#else
          return TkpFreeFontCache(interp, dpyTkWin, doClear);
#endif /* __WIN32__ */
      }
#endif /* KANJI */
      case FONT_CONFIGURE: {
          int result;
          char *string;
          Tcl_Obj *objPtr;
          NamedFont *nfPtr;
          Tcl_HashEntry *namedHashPtr;

#ifdef KANJI
          Tk_Font tkfont = NULL;
          TkFontAttributes *faPtr = NULL;
#endif /* KANJI */
          if (objc < 3) {
            Tcl_WrongNumArgs(interp, 2, objv, "fontname ?options?");
            return TCL_ERROR;
          }
          string = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL));
          namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
          nfPtr = NULL;       /* lint. */
          if (namedHashPtr != NULL) {
            nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
          }
          if ((namedHashPtr == NULL) || (nfPtr->deletePending != 0)) {
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
                  "\" doesn't exist", NULL);
            return TCL_ERROR;
          }
#ifdef KANJI
          tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
          faPtr = &nfPtr->fa;
#endif /* KANJI */
          if (objc == 3) {
            objPtr = NULL;
          } else if (objc == 4) {
            objPtr = objv[3];
          } else {
#ifdef KANJI
            result = ConfigAttributesObj(interp, tkwin, objc - 3,
                  objv + 3, faPtr);
            memcpy((VOID *)(GetFontAttributes(tkfont)), (VOID *)faPtr,
                   sizeof(TkFontAttributes));
#else
            result = ConfigAttributesObj(interp, tkwin, objc - 3,
                  objv + 3, &nfPtr->fa);

#endif /* KANJI */
            UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
#ifdef KANJI
            Tk_FreeFont(tkfont);
#endif /* KANJI */
            return result;
          }
#ifdef KANJI
          if (Tk_FontType(tkfont) == TK_FONT_COMPOUND ||
            Tk_FontIsDescendant(tkfont)) {
            result = GetAttributeInfoObj(interp,
                                   GetFontAttributes(tkfont), objPtr);
          } else {
            result = GetAttributeInfoObj(interp, faPtr, objPtr);
          }
          Tk_FreeFont(tkfont);
          return result;
#else
          return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr);
#endif /* KANJI */
      }
      case FONT_CREATE: {
          int skip, i;
          char *name;
          char buf[32];
          TkFontAttributes fa;
          Tcl_HashEntry *namedHashPtr;
#ifdef KANJI
          int ret = TCL_OK;
          Tk_Font copy = NULL;
          Tk_Font ascii = NULL;
          Tk_Font kanji = NULL;
#endif /* KANJI */

          skip = 3;
          if (objc < 3) {
            name = NULL;
          } else {
            name = Tcl_GetStringFromObj(objv[2], NULL);
#ifdef KANJI
            for (i = 0; i < FONT_NUMFIELDS; i++) {
                if (strcmp(fontOpt[i], name) == 0) {
                  name = NULL;
                  break;
                }
            }
            if (name != NULL && strncmp("-copy", name, 5) == 0) {
                name = NULL;
            }
#else
            if (name[0] == '-') {
                name = NULL;
            }
#endif /* KANJI */
          }
          if (name == NULL) {
            /*
             * No font name specified.  Generate one of the form "fontX".
             */

            for (i = 1; ; i++) {
                sprintf(buf, "font%d", i);
                namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable,
                      Tk_GetUid(buf));
                if (namedHashPtr == NULL) {
                  break;
                }
            }
            name = buf;
            skip = 2;
          }
          TkInitFontAttributes(&fa);
#ifdef KANJI
          if ((objv[skip] != NULL) &&
            (!strcmp(Tcl_GetStringFromObj(objv[skip], NULL), "-compound"))) {
            int numFonts;
            char **fontNames;
            Tk_Font tmp;
            Tk_Uid tmpName;
            char compoundName[1024];

            skip++;

            if (objv[skip] == NULL) {
                Tcl_WrongNumArgs(interp, objc, objv, 
                             "create ?fontname? -compound fontlist ?options?");
                return TCL_ERROR;
            }
            if (Tcl_SplitList(interp, Tcl_GetStringFromObj(objv[skip], NULL), 
                          &numFonts, &fontNames) != TCL_OK) {
                return TCL_ERROR;
            }
            /*
             * Currently, ascii and kanji only.
             */
#define MAX_COMPOUND    2
            if (numFonts > MAX_COMPOUND) numFonts = MAX_COMPOUND;

            for (i = 0; i < numFonts; i++) {
                tmpName = Tk_GetUid(fontNames[i]);
                tmp = Tk_GetFont(interp, tkwin, tmpName);
                if (tmp == NULL) {
                    Tcl_Free((char *)fontNames);
                  return TCL_ERROR;
                }
                if (Tk_FontType(tmp) == TK_FONT_COMPOUND) {
                  Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
                                    "font \"", tmpName, 
                                    "\" is already compounded.", NULL);
                    Tcl_Free((char *)fontNames);
                  Tk_FreeFont(tmp);
                  return TCL_ERROR;
                } else if (Tk_FontType(tmp) == TK_FONT_GENERIC) {
                  if (ascii == NULL) {
                      ascii = tmp;
                      fa.asciiFontName = tmpName;
                  } else {
                      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                                       "ascii font is alread specified.", NULL);
                      Tcl_Free((char *)fontNames);
                      Tk_FreeFont(tmp);
                      Tk_FreeFont(ascii);
                      return TCL_ERROR;
                  }
                } else if (Tk_FontType(tmp) == TK_FONT_2BYTES) {
                  if (kanji == NULL) {
                      kanji = tmp;
                      fa.kanjiFontName = tmpName;
                  } else {
#ifdef __WIN32__
                      /* assume {ascii kanji} order */
                      ascii = kanji;
                      fa.asciiFontName = fa.kanjiFontName;
                      kanji = tmp;
                      fa.kanjiFontName = tmpName;
#else
                      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                                       "kanji font is alread specified.", NULL);
                      Tcl_Free((char *)fontNames);
                      Tk_FreeFont(tmp);
                      Tk_FreeFont(kanji);
                      return TCL_ERROR;
#endif /* __WIN32__ */
                  }
                } else {
                  Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                                     "Can't handle the charset of font \"",
                                     tmpName, "\".", NULL);
                  Tcl_Free((char *)fontNames);
                  Tk_FreeFont(tmp);
                  return TCL_ERROR;
                }
            }

            if (ascii == NULL) {
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                                 "ascii font is not specified.", NULL);
                if (kanji != NULL) {
                  Tk_FreeFont(kanji);
                }
                Tcl_Free((char *)fontNames);
                return TCL_ERROR;
            }
            if (kanji == NULL) {
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                                 "2 bytes font is not specified.", NULL);
                if (ascii != NULL) {
                  Tk_FreeFont(ascii);
                }
                Tcl_Free((char *)fontNames);
                return TCL_ERROR;
            }

            sprintf(compoundName, "%s:%s", 
                  (Tk_FontFoundry(ascii) == NULL) ? "{}" : Tk_FontFoundry(ascii),
                  (Tk_FontFoundry(kanji) == NULL) ? "{}" : Tk_FontFoundry(kanji));
            fa.foundry = Tk_GetUid(compoundName);
            sprintf(compoundName, "%s:%s", Tk_FontFamily(ascii), Tk_FontFamily(kanji));
            fa.family = Tk_GetUid(compoundName);
            sprintf(compoundName, "%s:%s", Tk_FontCharset(ascii), Tk_FontCharset(kanji));
            fa.charset = Tk_GetUid(compoundName);
            fa.fontType = TK_FONT_COMPOUND;
            fa.pointsize = Tk_FontSize(kanji);
            if (fa.pointsize < 0) {
                fa.pointsize = TkpConvertPixelToPoint(tkwin, -(fa.pointsize));
            }
            {
                int aP = Tk_FontSize(ascii);
                if (aP < 0) {
                  aP = TkpConvertPixelToPoint(tkwin, -aP);
                }
                fa.pointAdjust = (double)((double)aP / (double)fa.pointsize);
            }
            skip++;
            Tcl_Free((char *)fontNames);
          } else if ((objv[skip] != NULL) && 
                   (!strcmp(Tcl_GetStringFromObj(objv[skip], NULL), "-copy"))) {
            skip++;
            if (objv[skip] == NULL) {
                Tcl_WrongNumArgs(interp, objc, objv, 
                             "create ?fontname? -copy fontname ?options?");
                return TCL_ERROR;
            }
            copy = Tk_GetFontFromObj(interp, tkwin, objv[skip]);
            if (copy == NULL) {
                return TCL_ERROR;
            }
            skip++;
            memcpy((VOID *)&fa, (VOID *)&((TkFont *)copy)->fa,
                   sizeof(TkFontAttributes));
          }
#endif /* KANJI */
          if (ConfigAttributesObj(interp, tkwin, objc - skip, objv + skip,
                &fa) != TCL_OK) {
#ifdef KANJI
            ret = TCL_ERROR;
            goto CreateEnd;
#else
            return TCL_ERROR;
#endif /* KANJI */
          }
          if (TkCreateNamedFont(interp, tkwin, name, &fa) != TCL_OK) {
#ifdef KANJI
            ret = TCL_ERROR;
            goto CreateEnd;
#else
            return TCL_ERROR;
#endif /* KANJI */
          }
#ifdef KANJI
          if (kanji != NULL && ascii != NULL) {
            TkpGetFontFromAttributes((TkFont *)ascii, tkwin, &((TkFont *)ascii)->fa);
            TkpGetFontFromAttributes((TkFont *)kanji, tkwin, &((TkFont *)kanji)->fa);
          }
#endif /* KANJI */
          Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
#ifdef KANJI
          CreateEnd:
          if (copy != NULL) {
            Tk_FreeFont(copy);
          }
          return ret;
#endif /* KANJI */
          break;
      }
      case FONT_DELETE: {
          int i;
          char *string;
          NamedFont *nfPtr;
          Tcl_HashEntry *namedHashPtr;

          /*
           * Delete the named font.  If there are still widgets using this
           * font, then it isn't deleted right away.
           */

          if (objc < 3) {
            Tcl_WrongNumArgs(interp, 2, objv, "fontname ?fontname ...?");
            return TCL_ERROR;
          }
          for (i = 2; i < objc; i++) {
            string = Tk_GetUid(Tcl_GetStringFromObj(objv[i], NULL));
            namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string);
            if (namedHashPtr == NULL) {
                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "named font \"", string,
                      "\" doesn't exist", (char *) NULL);
                return TCL_ERROR;
            }
            nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
            if (nfPtr->refCount != 0) {
                nfPtr->deletePending = 1;
            } else {
                Tcl_DeleteHashEntry(namedHashPtr);
                ckfree((char *) nfPtr);
            }
          }
          break;
      }
      case FONT_FAMILIES: {
          int skip;

          skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin);
          if (skip < 0) {
            return TCL_ERROR;
          }
          if (objc - skip != 2) {
            Tcl_WrongNumArgs(interp, 2, objv, "?-displayof window?");
            return TCL_ERROR;
          }
          TkpGetFontFamilies(interp, tkwin);
          break;
      }
#ifdef KANJI
      case FONT_FAILSAFE: {
          char *string = NULL;
          if (objc < 3) {
            string = TkpGetDefaultFont();
          } else {
            string = Tcl_GetStringFromObj(objv[2], NULL);
            if (TkpSetDefaultFont(interp, tkwin, string) != TCL_OK) {
                return TCL_ERROR;
            }
          }
          Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), string, NULL);
          break;
      }
#endif /* KANJI */
      case FONT_MEASURE: {
          char *string;
          Tk_Font tkfont;
          int length, skip;
          
          skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
          if (skip < 0) {
            return TCL_ERROR;
          }
          if (objc - skip != 4) {
            Tcl_WrongNumArgs(interp, 2, objv,
                  "font ?-displayof window? text");
            return TCL_ERROR;
          }
          tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
          if (tkfont == NULL) {
            return TCL_ERROR;
          }
          string = Tcl_GetStringFromObj(objv[3 + skip], &length);
#ifdef KANJI
          {
            wchar *wTmp = Tcl_GetWStr(NULL, string, NULL);
            length = Tcl_WStrlen(wTmp);
            Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_WTextWidth(tkfont, wTmp, length));
            Tcl_FreeWStr(wTmp);
          }
#else     
          Tcl_SetIntObj(Tcl_GetObjResult(interp), Tk_TextWidth(tkfont, string, length));
#endif /* KANJI */
          Tk_FreeFont(tkfont);
          break;
      }
      case FONT_METRICS: {
          char buf[64];
          Tk_Font tkfont;
          int skip, index, i;
          CONST TkFontMetrics *fmPtr;
          static char *switches[] = {
            "-ascent", "-descent", "-linespace", "-fixed", NULL
          };

          skip = TkGetDisplayOf(interp, objc - 3, objv + 3, &tkwin);
          if (skip < 0) {
            return TCL_ERROR;
          }
          if ((objc < 3) || ((objc - skip) > 4)) {
            Tcl_WrongNumArgs(interp, 2, objv,
                  "font ?-displayof window? ?option?");
            return TCL_ERROR;
          }
          tkfont = Tk_GetFontFromObj(interp, tkwin, objv[2]);
          if (tkfont == NULL) {
            return TCL_ERROR;
          }
          objc -= skip;
          objv += skip;
          fmPtr = GetFontMetrics(tkfont);
          if (objc == 3) {
            sprintf(buf, "-ascent %d -descent %d -linespace %d -fixed %d",
                  fmPtr->ascent, fmPtr->descent,
                  fmPtr->ascent + fmPtr->descent,
                  fmPtr->fixed);
            Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, -1);
          } else {
            if (Tcl_GetIndexFromObj(interp, objv[3], switches,
                  "metric", 0, &index) != TCL_OK) {
                Tk_FreeFont(tkfont);
                return TCL_ERROR;
            }
            i = 0;                  /* Needed only to prevent compiler
                               * warning. */
            switch (index) {
                case 0: i = fmPtr->ascent;                  break;
                case 1: i = fmPtr->descent;                 break;
                case 2: i = fmPtr->ascent + fmPtr->descent; break;
                case 3: i = fmPtr->fixed;             break;
            }
            Tcl_SetIntObj(Tcl_GetObjResult(interp), i);
          }
          Tk_FreeFont(tkfont);
          break;
      }
      case FONT_NAMES: {
          char *string;
          Tcl_Obj *strPtr;
          NamedFont *nfPtr;
          Tcl_HashSearch search;
          Tcl_HashEntry *namedHashPtr;
          
          if (objc != 2) {
            Tcl_WrongNumArgs(interp, 1, objv, "names");
            return TCL_ERROR;
          }
          namedHashPtr = Tcl_FirstHashEntry(&fiPtr->namedTable, &search);
          while (namedHashPtr != NULL) {
            nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
            if (nfPtr->deletePending == 0) {
                string = Tcl_GetHashKey(&fiPtr->namedTable, namedHashPtr);
                strPtr = Tcl_NewStringObj(string, -1);
                Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), strPtr);
            }
            namedHashPtr = Tcl_NextHashEntry(&search);
          }
          break;
      }
    }
    return TCL_OK;
}

/*
 *---------------------------------------------------------------------------
 *
 * UpdateDependantFonts, TheWorldHasChanged, RecomputeWidgets --
 *
 *    Called when the attributes of a named font changes.  Updates all
 *    the instantiated fonts that depend on that named font and then
 *    uses the brute force approach and prepares every widget to
 *    recompute its geometry.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Things get queued for redisplay.
 *
 *---------------------------------------------------------------------------
 */

static void
UpdateDependantFonts(fiPtr, tkwin, namedHashPtr)
    TkFontInfo *fiPtr;        /* Info about application's fonts. */
    Tk_Window tkwin;          /* A window in the application. */
    Tcl_HashEntry *namedHashPtr;/* The named font that is changing. */
{
    Tcl_HashEntry *cacheHashPtr;
    Tcl_HashSearch search;
    TkFont *fontPtr;
    NamedFont *nfPtr;

    nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
    if (nfPtr->refCount == 0) {
      /*
       * Well nobody's using this named font, so don't have to tell
       * any widgets to recompute themselves.
       */

      return;
    }


    cacheHashPtr = Tcl_FirstHashEntry(&fiPtr->fontCache, &search);
    while (cacheHashPtr != NULL) {
      fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
      if (fontPtr->namedHashPtr == namedHashPtr) {
#ifdef KANJI
          if (Tk_FontType(fontPtr) != TK_FONT_COMPOUND) {
            /* first, update itself, then update parents if exists. */
            TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
            if (fontPtr->parent.parent != NULL) {
                Tk_UpdateCompoundParent(tkwin, (Tk_Font)fontPtr);
            }
          } else {
            /* first, update descendants, next update parents. */
            Tk_UpdateCompoundDescendants(tkwin, (Tk_Font)fontPtr);
            TkpUpdateCompoundFont(fontPtr, &nfPtr->fa);
          }
#else
          TkpGetFontFromAttributes(fontPtr, tkwin, &nfPtr->fa);
#endif /* KANJI */
          if (fiPtr->updatePending == 0) {
            fiPtr->updatePending = 1;
            Tcl_DoWhenIdle(TheWorldHasChanged, (ClientData) fiPtr);
          }
      }
      cacheHashPtr = Tcl_NextHashEntry(&search);
    }
}

static void
TheWorldHasChanged(clientData)
    ClientData clientData;    /* Info about application's fonts. */
{
    TkFontInfo *fiPtr;

    fiPtr = (TkFontInfo *) clientData;
    fiPtr->updatePending = 0;
#ifdef COMPOUND_DEBUG
    fprintf(stderr, "\n\nWorld Changed !!\n\n");
#endif

    RecomputeWidgets(fiPtr->mainPtr->winPtr);
}

static void
RecomputeWidgets(winPtr)
    TkWindow *winPtr;         /* Window to which command is sent. */
{
    if ((winPtr->classProcsPtr != NULL)
          && (winPtr->classProcsPtr->geometryProc != NULL)) {
      (*winPtr->classProcsPtr->geometryProc)(winPtr->instanceData);
    }
    for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
      RecomputeWidgets(winPtr);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkCreateNamedFont --
 *
 *    Create the specified named font with the given attributes in the
 *    named font table associated with the interp.  
 *
 * Results:
 *    Returns TCL_OK if the font was successfully created, or TCL_ERROR
 *    if the named font already existed.  If TCL_ERROR is returned, an
 *    error message is left in interp->result.
 *
 * Side effects:
 *    Assume there used to exist a named font by the specified name, and
 *    that the named font had been deleted, but there were still some
 *    widgets using the named font at the time it was deleted.  If a
 *    new named font is created with the same name, all those widgets
 *    that were using the old named font will be redisplayed using
 *    the new named font's attributes.
 *
 *---------------------------------------------------------------------------
 */

int
TkCreateNamedFont(interp, tkwin, name, faPtr)
    Tcl_Interp *interp;       /* Interp for error return. */
    Tk_Window tkwin;          /* A window associated with interp. */
    CONST char *name;         /* Name for the new named font. */
    TkFontAttributes *faPtr;  /* Attributes for the new named font. */
{
    TkFontInfo *fiPtr;
    Tcl_HashEntry *namedHashPtr;
    int new;
    NamedFont *nfPtr;    

    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;

    name = Tk_GetUid(name);
    namedHashPtr = Tcl_CreateHashEntry(&fiPtr->namedTable, name, &new);
                
    if (new == 0) {
      nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
      if (nfPtr->deletePending == 0) {
          interp->result[0] = '\0';
          Tcl_AppendResult(interp, "font \"", name,
                "\" already exists", (char *) NULL);
          return TCL_ERROR;
      }

      /*
       * Recreating a named font with the same name as a previous
       * named font.  Some widgets were still using that named
       * font, so they need to get redisplayed.
       */

      nfPtr->fa = *faPtr;
      nfPtr->deletePending = 0;
      UpdateDependantFonts(fiPtr, tkwin, namedHashPtr);
      return TCL_OK;
    }

    nfPtr = (NamedFont *) ckalloc(sizeof(NamedFont));
    nfPtr->deletePending = 0;
    Tcl_SetHashValue(namedHashPtr, nfPtr);
    nfPtr->fa = *faPtr;
    nfPtr->refCount = 0;      
    nfPtr->deletePending = 0;
    return TCL_OK;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_GetFont -- 
 *
 *    Given a string description of a font, map the description to a
 *    corresponding Tk_Font that represents the font.
 *
 * Results:
 *    The return value is token for the font, or NULL if an error
 *    prevented the font from being created.  If NULL is returned, an
 *    error message will be left in interp->result.
 *
 * Side effects:
 *    Calls Tk_GetFontFromObj(), which modifies interp's result object,
 *    then copies the string from the result object into interp->result.
 *    This procedure will go away when Tk_ConfigureWidget() is
 *    made into an object command.
 *
 *---------------------------------------------------------------------------
 */

Tk_Font
Tk_GetFont(interp, tkwin, string)
    Tcl_Interp *interp;       /* Interp for database and error return. */
    Tk_Window tkwin;          /* For display on which font will be used. */
    CONST char *string;       /* String describing font, as: named font,
                         * native format, or parseable string. */
{
    Tcl_Obj *strPtr;
    Tk_Font tkfont;
    
    strPtr = Tcl_NewStringObj((char *) string, -1);
    
    tkfont = Tk_GetFontFromObj(interp, tkwin, strPtr);
    if (tkfont == NULL) {
      Tcl_SetResult(interp,
              Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL),
            TCL_VOLATILE);
    }

    Tcl_DecrRefCount(strPtr); /* done with object */
    return tkfont;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_GetFontFromObj -- 
 *
 *    Given a string description of a font, map the description to a
 *    corresponding Tk_Font that represents the font.
 *
 * Results:
 *    The return value is token for the font, or NULL if an error
 *    prevented the font from being created.  If NULL is returned, an
 *    error message will be left in interp's result object.
 *
 * Side effects:
 *    The font is added to an internal database with a reference
 *    count.  For each call to this procedure, there should eventually
 *    be a call to Tk_FreeFont() so that the database is cleaned up when
 *    fonts aren't in use anymore.
 *
 *---------------------------------------------------------------------------
 */

Tk_Font
Tk_GetFontFromObj(interp, tkwin, objPtr)
    Tcl_Interp *interp;       /* Interp for database and error return. */
    Tk_Window tkwin;          /* For display on which font will be used. */
    Tcl_Obj *objPtr;          /* Object describing font, as: named font,
                         * native format, or parseable string. */
{
    TkFontInfo *fiPtr;
    CachedFontKey key;
    Tcl_HashEntry *cacheHashPtr, *namedHashPtr;
    TkFont *fontPtr;
#if defined(KANJI) && !defined(__WIN32__)
    int new;
#else
    int new, descent;
#endif /* KANJI */
    NamedFont *nfPtr;
    char *string;
    
    fiPtr = ((TkWindow *) tkwin)->mainPtr->fontInfoPtr;
    string = Tcl_GetStringFromObj(objPtr, NULL);

    key.display = Tk_Display(tkwin);
    key.string = Tk_GetUid(string);
    cacheHashPtr = Tcl_CreateHashEntry(&fiPtr->fontCache, (char *) &key, &new);

    if (new == 0) {
      /*
       * We have already constructed a font with this description for
       * this display.  Bump the reference count of the cached font.
       */

      fontPtr = (TkFont *) Tcl_GetHashValue(cacheHashPtr);
      fontPtr->refCount++;
      return (Tk_Font) fontPtr;
    }

    namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, key.string);
    if (namedHashPtr != NULL) {
      /*
       * Construct a font based on a named font.
       */

      nfPtr = (NamedFont *) Tcl_GetHashValue(namedHashPtr);
      nfPtr->refCount++;

      fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &nfPtr->fa);
    } else {
      /*
       * Native font?
       */

      fontPtr = TkpGetNativeFont(tkwin, string);
      if (fontPtr == NULL) {
          TkFontAttributes fa;

          TkInitFontAttributes(&fa);
          if (ParseFontNameObj(interp, tkwin, objPtr, &fa) != TCL_OK) {
            Tcl_DeleteHashEntry(cacheHashPtr);
            return NULL;
          }

          /*
           * String contained the attributes inline.
           */

          fontPtr = TkpGetFontFromAttributes(NULL, tkwin, &fa);
      }
    }
    Tcl_SetHashValue(cacheHashPtr, fontPtr);

    fontPtr->refCount       = 1;
    fontPtr->cacheHashPtr   = cacheHashPtr;
    fontPtr->namedHashPtr   = namedHashPtr;

#ifdef KANJI
    switch(Tk_FontType(fontPtr)) {
      case TK_FONT_COMPOUND:
      case TK_FONT_GENERIC: {
          wchar wTmp = '0';
          Tk_MeasureWChars((Tk_Font) fontPtr, &wTmp, 1, 0, 0, &fontPtr->tabWidth);
          break;
      }
      case TK_FONT_2BYTES: {
          wchar wTmp[] = { 0xa3b0, 0x0000 };  /* Zenkaku '0' (EUC) */
          Tk_MeasureWChars((Tk_Font) fontPtr, wTmp, 1, 0, 0, &fontPtr->tabWidth);
          break;
      }
    }
#else
    Tk_MeasureChars((Tk_Font) fontPtr, "0", 1, 0, 0, &fontPtr->tabWidth);
#endif /* KANJI */
    if (fontPtr->tabWidth == 0) {
      fontPtr->tabWidth = fontPtr->fm.maxWidth;
    }
    fontPtr->tabWidth *= 8;

    /*
     * Make sure the tab width isn't zero (some fonts may not have enough
     * information to set a reasonable tab width).
     */

    if (fontPtr->tabWidth == 0) {
      fontPtr->tabWidth = 1;
    }

#if defined(KANJI) && !defined(__WIN32__) /* ??? how about win32? */
    /*
     * These emulation should be done in platform specific font methods.
     */
#else
    /*
     * Get information used for drawing underlines in generic code on a
     * non-underlined font.
     */
    
    descent = fontPtr->fm.descent;
    fontPtr->underlinePos = descent / 2;
    fontPtr->underlineHeight = fontPtr->fa.pointsize / 10;
    if (fontPtr->underlineHeight == 0) {
      fontPtr->underlineHeight = 1;
    }
    if (fontPtr->underlinePos + fontPtr->underlineHeight > descent) {
      /*
       * If this set of values would cause the bottom of the underline
       * bar to stick below the descent of the font, jack the underline
       * up a bit higher.
       */

      fontPtr->underlineHeight = descent - fontPtr->underlinePos;
      if (fontPtr->underlineHeight == 0) {
          fontPtr->underlinePos--;
          fontPtr->underlineHeight = 1;
      }
    }
#endif /* KANJI */
    
    return (Tk_Font) fontPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_NameOfFont --
 *
 *    Given a font, return a textual string identifying it.
 *
 * Results:
 *    The return value is the description that was passed to
 *    Tk_GetFont() to create the font.  The storage for the returned
 *    string is only guaranteed to persist until the font is deleted.
 *    The caller should not modify this string.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

char *
Tk_NameOfFont(tkfont)
    Tk_Font tkfont;           /* Font whose name is desired. */
{
    TkFont *fontPtr;
    Tcl_HashEntry *hPtr;
    CachedFontKey *keyPtr;

    fontPtr = (TkFont *) tkfont;
    hPtr = fontPtr->cacheHashPtr;

    keyPtr = (CachedFontKey *) Tcl_GetHashKey(hPtr->tablePtr, hPtr);
    return (char *) keyPtr->string;    
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FreeFont -- 
 *
 *    Called to release a font allocated by Tk_GetFont().
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The reference count associated with font is decremented, and
 *    only deallocated when no one is using it.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_FreeFont(tkfont)
    Tk_Font tkfont;           /* Font to be released. */
{
    TkFont *fontPtr;
    NamedFont *nfPtr;

    if (tkfont == NULL) {
      return;
    }
    fontPtr = (TkFont *) tkfont;
    fontPtr->refCount--;
    if (fontPtr->refCount == 0) {
      if (fontPtr->namedHashPtr != NULL) {
          /*
           * The font is being deleted.  Determine if the associated named
           * font definition should and/or can be deleted too.
           */

          nfPtr = (NamedFont *) Tcl_GetHashValue(fontPtr->namedHashPtr);
          nfPtr->refCount--;
          if ((nfPtr->refCount == 0) && (nfPtr->deletePending != 0)) {
            Tcl_DeleteHashEntry(fontPtr->namedHashPtr);
            ckfree((char *) nfPtr);
          }
      }
      Tcl_DeleteHashEntry(fontPtr->cacheHashPtr);
      TkpDeleteFont(fontPtr);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FontId --
 *
 *    Given a font, return an opaque handle that should be selected
 *    into the XGCValues structure in order to get the constructed
 *    gc to use this font.  This procedure would go away if the
 *    XGCValues structure were replaced with a TkGCValues structure.
 *
 * Results:
 *    As above.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

Font
Tk_FontId(tkfont)
    Tk_Font tkfont;     /* Font that is going to be selected into GC. */
{
    TkFont *fontPtr;

    fontPtr = (TkFont *) tkfont;
#ifdef KANJI
    if (Tk_FontType(fontPtr) == TK_FONT_COMPOUND) {
      fontPtr = (TkFont *)fontPtr->asciiFontPtr;
    }
#endif /* KANJI */
    return fontPtr->fid;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_GetFontMetrics --
 *
 *    Returns overall ascent and descent metrics for the given font.
 *    These values can be used to space multiple lines of text and
 *    to align the baselines of text in different fonts.
 *
 * Results:
 *    If *heightPtr is non-NULL, it is filled with the overall height
 *    of the font, which is the sum of the ascent and descent.
 *    If *ascentPtr or *descentPtr is non-NULL, they are filled with
 *    the ascent and/or descent information for the font.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
void
Tk_GetFontMetrics(tkfont, fmPtr)
    Tk_Font tkfont;           /* Font in which metrics are calculated. */
    Tk_FontMetrics *fmPtr;    /* Pointer to structure in which font
                         * metrics for tkfont will be stored. */
{
    TkFont *fontPtr;

    fontPtr = (TkFont *) tkfont;
    fmPtr->ascent = fontPtr->fm.ascent;
    fmPtr->descent = fontPtr->fm.descent;
    fmPtr->linespace = fontPtr->fm.ascent + fontPtr->fm.descent;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_PostscriptFontName --
 *
 *    Given a Tk_Font, return the name of the corresponding Postscript
 *    font.
 *
 * Results:
 *    The return value is the pointsize of the given Tk_Font.
 *    The name of the Postscript font is appended to dsPtr.
 *
 * Side effects:
 *    If the font does not exist on the printer, the print job will
 *    fail at print time.  Given a "reasonable" Postscript printer,
 *    the following Tk_Font font families should print correctly:
 *
 *        Avant Garde, Arial, Bookman, Courier, Courier New, Geneva,
 *        Helvetica, Monaco, New Century Schoolbook, New York,
 *        Palatino, Symbol, Times, Times New Roman, Zapf Chancery,
 *        and Zapf Dingbats.
 *
 *    Any other Tk_Font font families may not print correctly
 *    because the computed Postscript font name may be incorrect.
 *
 *---------------------------------------------------------------------------
 */


int
Tk_PostscriptFontName(tkfont, dsPtr)
    Tk_Font tkfont;           /* Font in which text will be printed. */
    Tcl_DString *dsPtr;       /* Pointer to an initialized Tcl_DString to
                         * which the name of the Postscript font that
                         * corresponds to tkfont will be appended. */
{
    TkFont *fontPtr;
    char *family, *weightString, *slantString;
#ifdef KANJI
    char *charset;
#endif /* KANJI */
    char *src, *dest;
    int upper, len;

    len = Tcl_DStringLength(dsPtr);
    fontPtr = (TkFont *) tkfont;

    /*
     * Convert the case-insensitive Tk_Font family name to the
     * case-sensitive Postscript family name.  Take out any spaces and
     * capitalize the first letter of each word.
     */

    family = fontPtr->fa.family;
#ifdef KANJI
    charset = fontPtr->fa.charset;
#endif /* KANJI */
    if (strncasecmp(family, "itc ", 4) == 0) {
      family = family + 4;
    }
    if ((strcasecmp(family, "Arial") == 0)
          || (strcasecmp(family, "Geneva") == 0)) {
      family = "Helvetica";
    } else if ((strcasecmp(family, "Times New Roman") == 0)
          || (strcasecmp(family, "New York") == 0)) {
      family = "Times";
    } else if ((strcasecmp(family, "Courier New") == 0)
          || (strcasecmp(family, "Monaco") == 0)) {
      family = "Courier";
    } else if (strcasecmp(family, "AvantGarde") == 0) {
      family = "AvantGarde";
    } else if (strcasecmp(family, "ZapfChancery") == 0) {
      family = "ZapfChancery";
    } else if (strcasecmp(family, "ZapfDingbats") == 0) {
      family = "ZapfDingbats";
#ifdef KANJI
#ifdef __WIN32__
    } else if ((strcasecmp(family, "fixed") == 0 && strncasecmp(charset, "jisx0208", 8)) ||
             strcasecmp(charset, "shiftjis") == 0) {
      unsigned char gothic1[] = {0x83,0x53, 0x83,0x56, 0x83,0x62, 0x83,0x4e, 0x0};
      unsigned char gothic2[] = {0xba,0xde,0xbc,0xaf,0xb8, 0x0};
#ifdef USE_RKSJ_PSFONT
      if (strstr(family, (char*)gothic1) || strstr(family, (char*)gothic2)) {
          family = "GothicBBB-Medium-RKSJ-H";
      } else {
          family = "Ryumin-Light-RKSJ-H";
      }
#else
      if (strstr(family, (char*)gothic1) || strstr(family, (char*)gothic2)) {
          family = "GothicBBB-Medium-EUC-H";
      } else {
          family = "Ryumin-Light-EUC-H";
      }
#endif
#else /* __WIN32__ */
    } else if (strcasecmp(family, "fixed") == 0) {
      if (strncasecmp(charset, "jisx0208", 8) == 0) {
          /* Must be EUC encoding font. */
          family = "Ryumin-Light-EUC-H";
      } else {
          family = "Courier";
      }
#endif /* __WIN32__ */
#endif /* KANJI */
    } else {
      /*
       * Inline, capitalize the first letter of each word, lowercase the
       * rest of the letters in each word, and then take out the spaces
       * between the words.  This may make the DString shorter, which is
       * safe to do.
       */

      Tcl_DStringAppend(dsPtr, family, -1);

      src = dest = Tcl_DStringValue(dsPtr) + len;
      upper = 1;
      for (; *src != '\0'; src++, dest++) {
          while (isspace(UCHAR(*src))) {
            src++;
            upper = 1;
          }
          *dest = *src;
          if ((upper != 0) && (islower(UCHAR(*src)))) {
            *dest = toupper(UCHAR(*src));
          }
          upper = 0;
      }
      *dest = '\0';
      Tcl_DStringSetLength(dsPtr, dest - Tcl_DStringValue(dsPtr));
      family = Tcl_DStringValue(dsPtr) + len;
    }
    if (family != Tcl_DStringValue(dsPtr) + len) {
      Tcl_DStringAppend(dsPtr, family, -1);
      family = Tcl_DStringValue(dsPtr) + len;
    }

    if (strcasecmp(family, "NewCenturySchoolbook") == 0) {
      Tcl_DStringSetLength(dsPtr, len);
      Tcl_DStringAppend(dsPtr, "NewCenturySchlbk", -1);
      family = Tcl_DStringValue(dsPtr) + len;
    }

    /*
     * Get the string to use for the weight.
     */

    weightString = NULL;
    if (fontPtr->fa.weight == TK_FW_NORMAL) {
      if (strcmp(family, "Bookman") == 0) {
          weightString = "Light";
      } else if (strcmp(family, "AvantGarde") == 0) {
          weightString = "Book";
      } else if (strcmp(family, "ZapfChancery") == 0) {
          weightString = "Medium";
      }
    } else {
      if ((strcmp(family, "Bookman") == 0)
            || (strcmp(family, "AvantGarde") == 0)) {
          weightString = "Demi";
      } else {
          weightString = "Bold";
      }
    }

    /*
     * Get the string to use for the slant.
     */

    slantString = NULL;
    if (fontPtr->fa.slant == TK_FS_ROMAN) {
      ;
    } else {
      if ((strcmp(family, "Helvetica") == 0)
            || (strcmp(family, "Courier") == 0)
            || (strcmp(family, "AvantGarde") == 0)) {
          slantString = "Oblique";
      } else {
          slantString = "Italic";
      }
    }

    /*
     * The string "Roman" needs to be added to some fonts that are not bold
     * and not italic.
     */

    if ((slantString == NULL) && (weightString == NULL)) {
      if ((strcmp(family, "Times") == 0) 
            || (strcmp(family, "NewCenturySchlbk") == 0)
            || (strcmp(family, "Palatino") == 0)) {
          Tcl_DStringAppend(dsPtr, "-Roman", -1);
      }
    } else {
      Tcl_DStringAppend(dsPtr, "-", -1);
      if (weightString != NULL) {
          Tcl_DStringAppend(dsPtr, weightString, -1);
      }
      if (slantString != NULL) {
          Tcl_DStringAppend(dsPtr, slantString, -1);
      }
    }

    return fontPtr->fa.pointsize;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_TextWidth --
 *
 *    A wrapper function for the more complicated interface of
 *    Tk_MeasureChars.  Computes how much space the given
 *    simple string needs.
 *
 * Results:
 *    The return value is the width (in pixels) of the given string.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_TextWidth(tkfont, string, numChars)
    Tk_Font tkfont;           /* Font in which text will be measured. */
    CONST char *string;       /* String whose width will be computed. */
    int numChars;       /* Number of characters to consider from
                         * string, or < 0 for strlen(). */
{
    int width;

    if (numChars < 0) {
      numChars = strlen(string);
    }
    Tk_MeasureChars(tkfont, string, numChars, 0, 0, &width);
    return width;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UnderlineChars --
 *
 *    This procedure draws an underline for a given range of characters
 *    in a given string.  It doesn't draw the characters (which are
 *    assumed to have been displayed previously); it just draws the
 *    underline.  This procedure would mainly be used to quickly
 *    underline a few characters without having to construct an
 *    underlined font.  To produce properly underlined text, the
 *    appropriate underlined font should be constructed and used. 
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Information gets displayed in "drawable".
 *
 *----------------------------------------------------------------------
 */

void
Tk_UnderlineChars(display, drawable, gc, tkfont, string, x, y, firstChar,
      lastChar)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context for actually drawing
                         * line. */
    Tk_Font tkfont;           /* Font used in GC;  must have been allocated
                         * by Tk_GetFont().  Used for character
                         * dimensions, etc. */
    CONST char *string;       /* String containing characters to be
                         * underlined or overstruck. */
    int x, y;                 /* Coordinates at which first character of
                         * string is drawn. */
    int firstChar;            /* Index of first character. */
    int lastChar;       /* Index of one after the last character. */
{
    TkFont *fontPtr;
    int startX, endX;

    fontPtr = (TkFont *) tkfont;
    
    Tk_MeasureChars(tkfont, string, firstChar, 0, 0, &startX);
    Tk_MeasureChars(tkfont, string, lastChar, 0, 0, &endX);

    XFillRectangle(display, drawable, gc, x + startX,
          y + fontPtr->underlinePos, (unsigned int) (endX - startX),
          (unsigned int) fontPtr->underlineHeight);
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_ComputeTextLayout --
 *
 *    Computes the amount of screen space needed to display a
 *    multi-line, justified string of text.  Records all the
 *    measurements that were done to determine to size and
 *    positioning of the individual lines of text; this information
 *    can be used by the Tk_DrawTextLayout() procedure to
 *    display the text quickly (without remeasuring it).
 *
 *    This procedure is useful for simple widgets that want to
 *    display single-font, multi-line text and want Tk to handle the
 *    details.
 *
 * Results:
 *    The return value is a Tk_TextLayout token that holds the
 *    measurement information for the given string.  The token is
 *    only valid for the given string.  If the string is freed,
 *    the token is no longer valid and must also be freed.  To free
 *    the token, call Tk_FreeTextLayout().
 *
 *    The dimensions of the screen area needed to display the text
 *    are stored in *widthPtr and *heightPtr.
 *
 * Side effects:
 *    Memory is allocated to hold the measurement information.  
 *
 *---------------------------------------------------------------------------
 */

Tk_TextLayout
Tk_ComputeTextLayout(tkfont, string, numChars, wrapLength, justify, flags,
      widthPtr, heightPtr)
    Tk_Font tkfont;           /* Font that will be used to display text. */
    CONST char *string;       /* String whose dimensions are to be
                         * computed. */
    int numChars;       /* Number of characters to consider from
                         * string, or < 0 for strlen(). */
    int wrapLength;           /* Longest permissible line length, in
                         * pixels.  <= 0 means no automatic wrapping:
                         * just let lines get as long as needed. */
    Tk_Justify justify;       /* How to justify lines. */
    int flags;                /* Flag bits OR-ed together.
                         * TK_IGNORE_TABS means that tab characters
                         * should not be expanded.  TK_IGNORE_NEWLINES
                         * means that newline characters should not
                         * cause a line break. */
    int *widthPtr;            /* Filled with width of string. */
    int *heightPtr;           /* Filled with height of string. */
{
    TkFont *fontPtr;
    CONST char *start, *end, *special;
    int n, y, charsThisChunk, maxChunks;
    int baseline, height, curX, newX, maxWidth;
    TextLayout *layoutPtr;
    LayoutChunk *chunkPtr;
    CONST TkFontMetrics *fmPtr;
#define MAX_LINES 50
    int staticLineLengths[MAX_LINES];
    int *lineLengths;
    int maxLines, curLine, layoutHeight;

    lineLengths = staticLineLengths;
    maxLines = MAX_LINES;
    
    fontPtr = (TkFont *) tkfont;
    fmPtr = &fontPtr->fm;

    height = fmPtr->ascent + fmPtr->descent;

    if (numChars < 0) {
      numChars = strlen(string);
    }

    maxChunks = 1;

    layoutPtr = (TextLayout *) ckalloc(sizeof(TextLayout)
          + (maxChunks - 1) * sizeof(LayoutChunk));
    layoutPtr->tkfont       = tkfont;
    layoutPtr->string       = string;
    layoutPtr->numChunks    = 0;

    baseline = fmPtr->ascent;
    maxWidth = 0;

    /*
     * Divide the string up into simple strings and measure each string.
     */

    curX = 0;

    end = string + numChars;
    special = string;

    flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
    flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;      
    curLine = 0;
    for (start = string; start < end; ) {
      if (start >= special) {
          /*
           * Find the next special character in the string.
           */

          for (special = start; special < end; special++) {
            if (!(flags & TK_IGNORE_NEWLINES)) {
                if ((*special == '\n') || (*special == '\r')) {
                  break;
                }
            }
            if (!(flags & TK_IGNORE_TABS)) {
                if (*special == '\t') {
                  break;
                }
            }
          }
      }

      /*
       * Special points at the next special character (or the end of the
       * string).  Process characters between start and special.
       */

      chunkPtr = NULL;
      if (start < special) {
          charsThisChunk = Tk_MeasureChars(tkfont, start, special - start,
                wrapLength - curX, flags, &newX);
          newX += curX;
          flags &= ~TK_AT_LEAST_ONE;
          if (charsThisChunk > 0) {
            chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
                  charsThisChunk, curX, newX, baseline);
                  
            start += charsThisChunk;
            curX = newX;
          }
      }

      if ((start == special) && (special < end)) {
          /*
           * Handle the special character.
           */

          chunkPtr = NULL;
          if (*special == '\t') {
            newX = curX + fontPtr->tabWidth;
            newX -= newX % fontPtr->tabWidth;
            NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
                  baseline)->numDisplayChars = -1;
            start++;
            if ((start < end) &&
                  ((wrapLength <= 0) || (newX <= wrapLength))) {
                /*
                 * More chars can still fit on this line.
                 */

                curX = newX;
                flags &= ~TK_AT_LEAST_ONE;
                continue;
            }
          } else {      
            NewChunk(&layoutPtr, &maxChunks, start, 1, curX, 1000000000,
                  baseline)->numDisplayChars = -1;
            start++;
            goto wrapLine;
          }
      }

      /*
       * No more characters are going to go on this line, either because
       * no more characters can fit or there are no more characters left.
       * Consume all extra spaces at end of line.  
       */

      while ((start < end) && isspace(UCHAR(*start))) {
          if (!(flags & TK_IGNORE_NEWLINES)) {
            if ((*start == '\n') || (*start == '\r')) {
                break;
            }
          }
          if (!(flags & TK_IGNORE_TABS)) {
            if (*start == '\t') {
                break;
            }
          }
          start++;
      }
      if (chunkPtr != NULL) {
          /*
           * Append all the extra spaces on this line to the end of the
           * last text chunk.
           */
          charsThisChunk = start - (chunkPtr->start + chunkPtr->numChars);
          if (charsThisChunk > 0) {
            chunkPtr->numChars += Tk_MeasureChars(tkfont,
                  chunkPtr->start + chunkPtr->numChars, charsThisChunk,
                  0, 0, &chunkPtr->totalWidth);
            chunkPtr->totalWidth += curX;
          }
      }

        wrapLine: 
      flags |= TK_AT_LEAST_ONE;

      /*
       * Save current line length, then move current position to start of
       * next line.
       */

      if (curX > maxWidth) {
          maxWidth = curX;
      }

      /*
       * Remember width of this line, so that all chunks on this line
       * can be centered or right justified, if necessary.
       */

      if (curLine >= maxLines) {
          int *newLengths;
          
          newLengths = (int *) ckalloc(2 * maxLines * sizeof(int));
          memcpy((void *) newLengths, lineLengths, maxLines * sizeof(int));
          if (lineLengths != staticLineLengths) {
              ckfree((char *) lineLengths);
          }
          lineLengths = newLengths;
          maxLines *= 2;
      }
      lineLengths[curLine] = curX;
      curLine++;

      curX = 0;
      baseline += height;
    }

    /*
     * If last line ends with a newline, then we need to make a 0 width
     * chunk on the next line.  Otherwise "Hello" and "Hello\n" are the
     * same height.
     */

    if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
      if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
          chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
                1000000000, baseline);
          chunkPtr->numDisplayChars = -1;
          baseline += height;
      }
    }     

    /*
     * Using maximum line length, shift all the chunks so that the lines are
     * all justified correctly.
     */
    
    curLine = 0;
    chunkPtr = layoutPtr->chunks;
    y = chunkPtr->y;
    for (n = 0; n < layoutPtr->numChunks; n++) {
      int extra;

      if (chunkPtr->y != y) {
          curLine++;
          y = chunkPtr->y;
      }
      extra = maxWidth - lineLengths[curLine];
      if (justify == TK_JUSTIFY_CENTER) {
          chunkPtr->x += extra / 2;
      } else if (justify == TK_JUSTIFY_RIGHT) {
          chunkPtr->x += extra;
      }
      chunkPtr++;
    }

    layoutPtr->width = maxWidth;
    layoutHeight = baseline - fmPtr->ascent;
    if (layoutPtr->numChunks == 0) {
      layoutHeight = height;

      /*
       * This fake chunk is used by the other procedures so that they can
       * pretend that there is a chunk with no chars in it, which makes
       * the coding simpler.
       */

      layoutPtr->numChunks = 1;
      layoutPtr->chunks[0].start          = string;
      layoutPtr->chunks[0].numChars       = 0;
      layoutPtr->chunks[0].numDisplayChars      = -1;
      layoutPtr->chunks[0].x              = 0;
      layoutPtr->chunks[0].y              = fmPtr->ascent;
      layoutPtr->chunks[0].totalWidth           = 0;
      layoutPtr->chunks[0].displayWidth   = 0;
    }

    if (widthPtr != NULL) {
      *widthPtr = layoutPtr->width;
    }
    if (heightPtr != NULL) {
      *heightPtr = layoutHeight;
    }
    if (lineLengths != staticLineLengths) {
      ckfree((char *) lineLengths);
    }

    return (Tk_TextLayout) layoutPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FreeTextLayout --
 *
 *    This procedure is called to release the storage associated with
 *    a Tk_TextLayout when it is no longer needed.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Memory is freed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_FreeTextLayout(textLayout)
    Tk_TextLayout textLayout; /* The text layout to be released. */
{
    TextLayout *layoutPtr;

    layoutPtr = (TextLayout *) textLayout;
    if (layoutPtr != NULL) {
      ckfree((char *) layoutPtr);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DrawTextLayout --
 *
 *    Use the information in the Tk_TextLayout token to display a
 *    multi-line, justified string of text.
 *
 *    This procedure is useful for simple widgets that need to
 *    display single-font, multi-line text and want Tk to handle
 *    the details.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Text drawn on the screen.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_DrawTextLayout(display, drawable, gc, layout, x, y, firstChar, lastChar)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context to use for drawing text. */
    Tk_TextLayout layout;     /* Layout information, from a previous call
                         * to Tk_ComputeTextLayout(). */
    int x, y;                 /* Upper-left hand corner of rectangle in
                         * which to draw (pixels). */
    int firstChar;            /* The index of the first character to draw
                         * from the given text item.  0 specfies the
                         * beginning. */
    int lastChar;       /* The index just after the last character
                         * to draw from the given text item.  A number
                         * < 0 means to draw all characters. */
{
    TextLayout *layoutPtr;
    int i, numDisplayChars, drawX;
    LayoutChunk *chunkPtr;

    layoutPtr = (TextLayout *) layout;
    if (layoutPtr == NULL) {
      return;
    }

    if (lastChar < 0) {
      lastChar = 100000000;
    }
    chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      numDisplayChars = chunkPtr->numDisplayChars;
      if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
          if (firstChar <= 0) {
            drawX = 0;
            firstChar = 0;
          } else {
            Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstChar,
                  0, 0, &drawX);
          }
          if (lastChar < numDisplayChars) {
            numDisplayChars = lastChar;
          }
          Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
                chunkPtr->start + firstChar, numDisplayChars - firstChar,
                x + chunkPtr->x + drawX, y + chunkPtr->y);
      }
      firstChar -= chunkPtr->numChars;
      lastChar -= chunkPtr->numChars;
      if (lastChar <= 0) {
          break;
      }
      chunkPtr++;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UnderlineTextLayout --
 *
 *    Use the information in the Tk_TextLayout token to display an
 *    underline below an individual character.  This procedure does
 *    not draw the text, just the underline.
 *
 *    This procedure is useful for simple widgets that need to
 *    display single-font, multi-line text with an individual
 *    character underlined and want Tk to handle the details.
 *    To display larger amounts of underlined text, construct
 *    and use an underlined font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Underline drawn on the screen.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_UnderlineTextLayout(display, drawable, gc, layout, x, y, underline)
    Display *display;         /* Display on which to draw. */
    Drawable drawable;        /* Window or pixmap in which to draw. */
    GC gc;              /* Graphics context to use for drawing text. */
    Tk_TextLayout layout;     /* Layout information, from a previous call
                         * to Tk_ComputeTextLayout(). */
    int x, y;                 /* Upper-left hand corner of rectangle in
                         * which to draw (pixels). */
    int underline;            /* Index of the single character to
                         * underline, or -1 for no underline. */
{
    TextLayout *layoutPtr;
    TkFont *fontPtr;
    int xx, yy, width, height;

    if ((Tk_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
          && (width != 0)) {
      layoutPtr = (TextLayout *) layout;
      fontPtr = (TkFont *) layoutPtr->tkfont;

      XFillRectangle(display, drawable, gc, x + xx, 
            y + yy + fontPtr->fm.ascent + fontPtr->underlinePos,
            (unsigned int) width, (unsigned int) fontPtr->underlineHeight);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_PointToChar --
 *
 *    Use the information in the Tk_TextLayout token to determine the
 *    character closest to the given point.  The point must be
 *    specified with respect to the upper-left hand corner of the
 *    text layout, which is considered to be located at (0, 0).
 *
 *    Any point whose y-value is less that 0 will be considered closest
 *    to the first character in the text layout; any point whose y-value
 *    is greater than the height of the text layout will be considered
 *    closest to the last character in the text layout.
 *
 *    Any point whose x-value is less than 0 will be considered closest
 *    to the first character on that line; any point whose x-value is
 *    greater than the width of the text layout will be considered
 *    closest to the last character on that line.
 *
 * Results:
 *    The return value is the index of the character that was
 *    closest to the point.  Given a text layout with no characters,
 *    the value 0 will always be returned, referring to a hypothetical
 *    zero-width placeholder character.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_PointToChar(layout, x, y)
    Tk_TextLayout layout;     /* Layout information, from a previous call
                         * to Tk_ComputeTextLayout(). */
    int x, y;                 /* Coordinates of point to check, with
                         * respect to the upper-left corner of the
                         * text layout. */
{
    TextLayout *layoutPtr;
    LayoutChunk *chunkPtr, *lastPtr;
    TkFont *fontPtr;
    int i, n, dummy, baseline, pos;

    if (y < 0) {
      /*
       * Point lies above any line in this layout.  Return the index of
       * the first char.
       */

      return 0;
    }

    /*
     * Find which line contains the point.
     */

    layoutPtr = (TextLayout *) layout;
    fontPtr = (TkFont *) layoutPtr->tkfont;
    lastPtr = chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      baseline = chunkPtr->y;
      if (y < baseline + fontPtr->fm.descent) {
          if (x < chunkPtr->x) {
            /*
             * Point is to the left of all chunks on this line.  Return
             * the index of the first character on this line.
             */

            return chunkPtr->start - layoutPtr->string;
          }
          if (x >= layoutPtr->width) {
            /*
             * If point lies off right side of the text layout, return
             * the last char in the last chunk on this line.  Without
             * this, it might return the index of the first char that
             * was located outside of the text layout.
             */

            x = INT_MAX;
          }

          /*
           * Examine all chunks on this line to see which one contains
           * the specified point.
           */

          lastPtr = chunkPtr;
          while ((i < layoutPtr->numChunks) && (chunkPtr->y == baseline))  {
            if (x < chunkPtr->x + chunkPtr->totalWidth) {
                /*
                 * Point falls on one of the characters in this chunk.
                 */

                if (chunkPtr->numDisplayChars < 0) {
                  /*
                   * This is a special chunk that encapsulates a single
                   * tab or newline char.
                   */

                  return chunkPtr->start - layoutPtr->string;
                }
                n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start,
                      chunkPtr->numChars, x + 1 - chunkPtr->x,
                      TK_PARTIAL_OK, &dummy);
                return (chunkPtr->start + n - 1) - layoutPtr->string;
            }
            lastPtr = chunkPtr;
            chunkPtr++;
            i++;
          }

          /*
           * Point is to the right of all chars in all the chunks on this
           * line.  Return the index just past the last char in the last
           * chunk on this line.
           */

          pos = (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
          if (i < layoutPtr->numChunks) {
            pos--;
          }
          return pos;
      }
      lastPtr = chunkPtr;
      chunkPtr++;
    }

    /*
     * Point lies below any line in this text layout.  Return the index
     * just past the last char.
     */

    return (lastPtr->start + lastPtr->numChars) - layoutPtr->string;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_CharBbox --
 *
 *    Use the information in the Tk_TextLayout token to return the
 *    bounding box for the character specified by index.  
 *
 *    The width of the bounding box is the advance width of the
 *    character, and does not include and left- or right-bearing.
 *    Any character that extends partially outside of the
 *    text layout is considered to be truncated at the edge.  Any
 *    character which is located completely outside of the text
 *    layout is considered to be zero-width and pegged against
 *    the edge.
 *
 *    The height of the bounding box is the line height for this font,
 *    extending from the top of the ascent to the bottom of the
 *    descent.  Information about the actual height of the individual
 *    letter is not available.
 *
 *    A text layout that contains no characters is considered to
 *    contain a single zero-width placeholder character.
 * 
 * Results:
 *    The return value is 0 if the index did not specify a character
 *    in the text layout, or non-zero otherwise.  In that case,
 *    *bbox is filled with the bounding box of the character.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_CharBbox(layout, index, xPtr, yPtr, widthPtr, heightPtr)
    Tk_TextLayout layout;   /* Layout information, from a previous call to
                       * Tk_ComputeTextLayout(). */
    int index;              /* The index of the character whose bbox is
                       * desired. */
    int *xPtr, *yPtr;       /* Filled with the upper-left hand corner, in
                       * pixels, of the bounding box for the character
                       * specified by index, if non-NULL. */
    int *widthPtr, *heightPtr;
                      /* Filled with the width and height of the
                       * bounding box for the character specified by
                       * index, if non-NULL. */
{
    TextLayout *layoutPtr;
    LayoutChunk *chunkPtr;
    int i, x, w;
    Tk_Font tkfont;
    TkFont *fontPtr;

    if (index < 0) {
      return 0;
    }

    layoutPtr = (TextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    tkfont = layoutPtr->tkfont;
    fontPtr = (TkFont *) tkfont;

    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->numDisplayChars < 0) {
          if (index == 0) {
            x = chunkPtr->x;
            w = chunkPtr->totalWidth;
            goto check;
          }
      } else if (index < chunkPtr->numChars) {
          if (xPtr != NULL) {
            Tk_MeasureChars(tkfont, chunkPtr->start, index, 0, 0, &x);
            x += chunkPtr->x;
          }
          if (widthPtr != NULL) {
            Tk_MeasureChars(tkfont, chunkPtr->start + index, 1, 0, 0, &w);
          }
          goto check;
      }
      index -= chunkPtr->numChars;
      chunkPtr++;
    }
    if (index == 0) {
      /*
       * Special case to get location just past last char in layout.
       */

      chunkPtr--;
      x = chunkPtr->x + chunkPtr->totalWidth;
      w = 0;
    } else {
      return 0;
    }

    /*
     * Ensure that the bbox lies within the text layout.  This forces all
     * chars that extend off the right edge of the text layout to have
     * truncated widths, and all chars that are completely off the right
     * edge of the text layout to peg to the edge and have 0 width.
     */
    check:
    if (yPtr != NULL) {
      *yPtr = chunkPtr->y - fontPtr->fm.ascent;
    }
    if (heightPtr != NULL) {
      *heightPtr = fontPtr->fm.ascent + fontPtr->fm.descent;
    }

    if (x > layoutPtr->width) {
      x = layoutPtr->width;
    }
    if (xPtr != NULL) {
      *xPtr = x;
    }
    if (widthPtr != NULL) {
      if (x + w > layoutPtr->width) {
          w = layoutPtr->width - x;
      }
      *widthPtr = w;
    }

    return 1;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DistanceToTextLayout --
 *
 *    Computes the distance in pixels from the given point to the
 *    given text layout.  Non-displaying space characters that occur
 *    at the end of individual lines in the text layout are ignored
 *    for hit detection purposes.
 *
 * Results:
 *    The return value is 0 if the point (x, y) is inside the text
 *    layout.  If the point isn't inside the text layout then the
 *    return value is the distance in pixels from the point to the
 *    text item.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_DistanceToTextLayout(layout, x, y)
    Tk_TextLayout layout;     /* Layout information, from a previous call
                         * to Tk_ComputeTextLayout(). */
    int x, y;                 /* Coordinates of point to check, with
                         * respect to the upper-left corner of the
                         * text layout (in pixels). */
{
    int i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
    LayoutChunk *chunkPtr;
    TextLayout *layoutPtr;
    TkFont *fontPtr;

    layoutPtr = (TextLayout *) layout;
    fontPtr = (TkFont *) layoutPtr->tkfont;
    ascent = fontPtr->fm.ascent;
    descent = fontPtr->fm.descent;
    
    minDist = 0;
    chunkPtr = layoutPtr->chunks;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->start[0] == '\n') {
          /*
           * Newline characters are not counted when computing distance
           * (but tab characters would still be considered).
           */

          chunkPtr++;
          continue;
      }

      x1 = chunkPtr->x;
      y1 = chunkPtr->y - ascent;
      x2 = chunkPtr->x + chunkPtr->displayWidth;
      y2 = chunkPtr->y + descent;

      if (x < x1) {
          xDiff = x1 - x;
      } else if (x >= x2) {
          xDiff = x - x2 + 1;
      } else {
          xDiff = 0;
      }

      if (y < y1) {
          yDiff = y1 - y;
      } else if (y >= y2) {
          yDiff = y - y2 + 1;
      } else {
          yDiff = 0;
      }
      if ((xDiff == 0) && (yDiff == 0)) {
          return 0;
      }
      dist = (int) hypot((double) xDiff, (double) yDiff);
      if ((dist < minDist) || (minDist == 0)) {
          minDist = dist;
      }
      chunkPtr++;
    }
    return minDist;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_IntersectTextLayout --
 *
 *    Determines whether a text layout lies entirely inside,
 *    entirely outside, or overlaps a given rectangle.  Non-displaying
 *    space characters that occur at the end of individual lines in
 *    the text layout are ignored for intersection calculations.
 *
 * Results:
 *    The return value is -1 if the text layout is entirely outside of
 *    the rectangle, 0 if it overlaps, and 1 if it is entirely inside
 *    of the rectangle.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
Tk_IntersectTextLayout(layout, x, y, width, height)
    Tk_TextLayout layout;     /* Layout information, from a previous call
                         * to Tk_ComputeTextLayout(). */
    int x, y;                 /* Upper-left hand corner, in pixels, of
                         * rectangular area to compare with text
                         * layout.  Coordinates are with respect to
                         * the upper-left hand corner of the text
                         * layout itself. */
    int width, height;        /* The width and height of the above
                         * rectangular area, in pixels. */
{
    int result, i, x1, y1, x2, y2;
    TextLayout *layoutPtr;
    LayoutChunk *chunkPtr;
    TkFont *fontPtr;
    int left, top, right, bottom;

    /*
     * Scan the chunks one at a time, seeing whether each is entirely in,
     * entirely out, or overlapping the rectangle.  If an overlap is
     * detected, return immediately; otherwise wait until all chunks have
     * been processed and see if they were all inside or all outside.
     */
    
    layoutPtr = (TextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    fontPtr = (TkFont *) layoutPtr->tkfont;

    left    = x;
    top         = y;
    right   = x + width;
    bottom  = y + height;

    result = 0;
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (chunkPtr->start[0] == '\n') {
          /*
           * Newline characters are not counted when computing area
           * intersection (but tab characters would still be considered).
           */

          chunkPtr++;
          continue;
      }

      x1 = chunkPtr->x;
      y1 = chunkPtr->y - fontPtr->fm.ascent;
      x2 = chunkPtr->x + chunkPtr->displayWidth;
      y2 = chunkPtr->y + fontPtr->fm.descent;

      if ((right < x1) || (left >= x2)
            || (bottom < y1) || (top >= y2)) {
          if (result == 1) {
            return 0;
          }
          result = -1;
      } else if ((x1 < left) || (x2 >= right)
            || (y1 < top) || (y2 >= bottom)) {
          return 0;
      } else if (result == -1) {
          return 0;
      } else {
          result = 1;
      }
      chunkPtr++;
    }
    return result;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_TextLayoutToPostscript --
 *
 *    Outputs the contents of a text layout in Postscript format.
 *    The set of lines in the text layout will be rendered by the user
 *    supplied Postscript function.  The function should be of the form:
 *
 *        justify x y string  function  --
 *
 *    Justify is -1, 0, or 1, depending on whether the following string
 *    should be left, center, or right justified, x and y is the
 *    location for the origin of the string, string is the sequence
 *    of characters to be printed, and function is the name of the
 *    caller-provided function; the function should leave nothing
 *    on the stack.
 *
 *    The meaning of the origin of the string (x and y) depends on
 *    the justification.  For left justification, x is where the
 *    left edge of the string should appear.  For center justification,
 *    x is where the center of the string should appear.  And for right
 *    justification, x is where the right edge of the string should
 *    appear.  This behavior is necessary because, for example, right
 *    justified text on the screen is justified with screen metrics.
 *    The same string needs to be justified with printer metrics on
 *    the printer to appear in the correct place with respect to other
 *    similarly justified strings.  In all circumstances, y is the
 *    location of the baseline for the string.
 *
 * Results:
 *    Interp->result is modified to hold the Postscript code that
 *    will render the text layout.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_TextLayoutToPostscript(interp, layout)
    Tcl_Interp *interp;       /* Filled with Postscript code. */
    Tk_TextLayout layout;     /* The layout to be rendered. */
{
#define MAXUSE 128
    char buf[MAXUSE+10];
    LayoutChunk *chunkPtr;
    int i, j, used, c, baseline;
    TextLayout *layoutPtr;

    layoutPtr = (TextLayout *) layout;
    chunkPtr = layoutPtr->chunks;
    baseline = chunkPtr->y;
    used = 0;
    buf[used++] = '(';
    for (i = 0; i < layoutPtr->numChunks; i++) {
      if (baseline != chunkPtr->y) {
          buf[used++] = ')';
          buf[used++] = '\n';
          buf[used++] = '(';
          baseline = chunkPtr->y;
      }
      if (chunkPtr->numDisplayChars <= 0) {
          if (chunkPtr->start[0] == '\t') {
            buf[used++] = '\\';
            buf[used++] = 't';
          }
      } else {
          for (j = 0; j < chunkPtr->numDisplayChars; j++) {
            c = UCHAR(chunkPtr->start[j]);
            if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20)
                  || (c >= UCHAR(0x7f))) {
                /*
                 * Tricky point:  the "03" is necessary in the sprintf
                 * below, so that a full three digits of octal are
                 * always generated.  Without the "03", a number
                 * following this sequence could be interpreted by
                 * Postscript as part of this sequence.
                 */

                sprintf(buf + used, "\\%03o", c);
                used += 4;
            } else {
                buf[used++] = c;
            }
            if (used >= MAXUSE) {
                buf[used] = '\0';
                Tcl_AppendResult(interp, buf, (char *) NULL);
                used = 0;
            }
          }
      }
      if (used >= MAXUSE) {
          /*
           * If there are a whole bunch of returns or tabs in a row,
           * then buf[] could get filled up.
           */
           
          buf[used] = '\0';
          Tcl_AppendResult(interp, buf, (char *) NULL);
          used = 0;
      }
      chunkPtr++;
    }
    buf[used++] = ')';
    buf[used++] = '\n';
    buf[used] = '\0';
    Tcl_AppendResult(interp, buf, (char *) NULL);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkInitFontAttributes --
 *
 *    Initialize the font attributes structure to contain sensible
 *    values.  This must be called before using any other font
 *    attributes functions.
 *
 * Results:
 *    None.
 *
 * Side effects.
 *    None.
 *
 *---------------------------------------------------------------------------
 */

void
TkInitFontAttributes(faPtr)
    TkFontAttributes *faPtr;  /* The attributes structure to initialize. */
{
    faPtr->family = NULL;
    faPtr->pointsize    = 0;
    faPtr->weight = TK_FW_NORMAL;
    faPtr->slant  = TK_FS_ROMAN;
    faPtr->underline    = 0;
    faPtr->overstrike   = 0;
#ifdef KANJI
    faPtr->setwidth     = TK_SW_NORMAL;
    faPtr->foundry      = NULL;
    faPtr->charset      = NULL;
    faPtr->fontType     = TK_FONT_GENERIC;
    faPtr->asciiFontName = NULL;
    faPtr->kanjiFontName = NULL;
    faPtr->pointAdjust  = 0.0;
#endif /* KANJI */
}

/*
 *---------------------------------------------------------------------------
 *
 * ConfigAttributesObj --
 *
 *    Process command line options to fill in fields of a properly
 *    initialized font attributes structure.
 *
 * Results:
 *    A standard Tcl return value.  If TCL_ERROR is returned, an
 *    error message will be left in interp's result object.
 *
 * Side effects:
 *    The fields of the font attributes structure get filled in with
 *    information from argc/argv.  If an error occurs while parsing,
 *    the font attributes structure will contain all modifications
 *    specified in the command line options up to the point of the
 *    error.
 *
 *---------------------------------------------------------------------------
 */

static int
ConfigAttributesObj(interp, tkwin, objc, objv, faPtr)
    Tcl_Interp *interp;       /* Interp for error return. */
    Tk_Window tkwin;          /* For display on which font will be used. */
    int objc;                 /* Number of elements in argv. */
    Tcl_Obj *CONST objv[];    /* Command line options. */
    TkFontAttributes *faPtr;  /* Font attributes structure whose fields
                         * are to be modified.  Structure must already
                         * be properly initialized. */
{
    int i, n, index;
    Tcl_Obj *value;
    char *option, *string;
    
    if (objc & 1) {
      string = Tcl_GetStringFromObj(objv[objc - 1], NULL);
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"",
            string, "\" option", (char *) NULL);
      return TCL_ERROR;
    }

#ifdef KANJI
    if (faPtr->fontType == TK_FONT_COMPOUND) {
      return ConfigCompoundAttributesObj(interp, tkwin, objc, objv, faPtr);
    }
#endif /* KANJI */

    for (i = 0; i < objc; i += 2) {
      option = Tcl_GetStringFromObj(objv[i], NULL);
      value = objv[i + 1];

      if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1,
            &index) != TCL_OK) {
          return TCL_ERROR;
      }
      switch (index) {
#ifdef KANJI
          case FONT_FOUNDRY:
            string = Tcl_GetStringFromObj(value, NULL);
            faPtr->foundry = Tk_GetUid(string);
            break;

#endif /* KANJI */
          case FONT_FAMILY:
            string = Tcl_GetStringFromObj(value, NULL);
            faPtr->family = Tk_GetUid(string);
            break;

          case FONT_SIZE:
            if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) {
                return TCL_ERROR;
            }
            faPtr->pointsize = n;
            break;

          case FONT_WEIGHT:
            string = Tcl_GetStringFromObj(value, NULL);
            n = TkFindStateNum(interp, option, weightMap, string);
            if (n == TK_FW_UNKNOWN) {
                return TCL_ERROR;
            }
            faPtr->weight = n;
            break;

          case FONT_SLANT: 
            string = Tcl_GetStringFromObj(value, NULL);
            n = TkFindStateNum(interp, option, slantMap, string);
            if (n == TK_FS_UNKNOWN) {
                return TCL_ERROR;
            }
            faPtr->slant = n;
            break;

          case FONT_UNDERLINE:
            if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
                return TCL_ERROR;
            }
            faPtr->underline = n;
            break;

          case FONT_OVERSTRIKE:
            if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
                return TCL_ERROR;
            }
            faPtr->overstrike = n;
            break;

#ifdef KANJI
          case FONT_CHARSET:
            string = Tcl_GetStringFromObj(value, NULL);
            faPtr->charset = Tk_GetUid(string);
            break;
            
#endif /* KANJI */
      }
    }
    return TCL_OK;
}
#ifdef KANJI

/*
 *---------------------------------------------------------------------------
 *
 * ConfigCompoundAttributesObj --
 *
 *    Process command line options to fill in fields of a properly
 *    initialized compound font and its descendant fonts attributes
 *    structure.
 *
 * Results:
 *    A standard Tcl return value.  If TCL_ERROR is returned, an
 *    error message will be left in interp's result object.
 *
 * Side effects:
 *    The fields of the font attributes structure get filled in with
 *    information from argc/argv.  If an error occurs while parsing,
 *    the font attributes structure will contain all modifications
 *    specified in the command line options up to the point of the
 *    error.
 *
 *---------------------------------------------------------------------------
 */

static int
ConfigCompoundAttributesObj(interp, tkwin, objc, objv, faPtr)
    Tcl_Interp *interp;       /* Interp for error return. */
    Tk_Window tkwin;          /* For display on which font will be used. */
    int objc;                 /* Number of elements in argv. */
    Tcl_Obj *CONST objv[];    /* Command line options. */
    TkFontAttributes *faPtr;  /* Font attributes structure whose fields
                         * are to be modified.  Structure must already
                         * be properly initialized. */
{
    int i, n;
    double d;
    char *option;
    Tcl_Obj *value;
    TkFont *ascii = NULL;
    TkFont *kanji = NULL;
    TkFontAttributes *asciiFaPtr = NULL;
    TkFontAttributes *kanjiFaPtr = NULL;
    int index;
    int newSize = faPtr->pointsize;
    double newAdjust = faPtr->pointAdjust;
    double aP;
    
    if (faPtr->fontType == TK_FONT_COMPOUND) {
      ascii = (TkFont *)Tk_GetFont(interp, tkwin, faPtr->asciiFontName);
      kanji = (TkFont *)Tk_GetFont(interp, tkwin, faPtr->kanjiFontName);
      asciiFaPtr = &(ascii->fa);
      kanjiFaPtr = &(kanji->fa);
    } else {
      panic("ConfigCompoundAttributesObj() was called for non-compound fonts.");
    }
    
    if (objc & 1) {
      char *string = Tcl_GetStringFromObj(objv[objc - 1], NULL);
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "missing value for \"",
            string, "\" option", (char *) NULL);
      return TCL_ERROR;
    }

    for (i = 0; i < objc; i += 2) {
      option = Tcl_GetStringFromObj(objv[i], NULL);
      value = objv[i + 1];

      if (Tcl_GetIndexFromObj(interp, objv[i], fontOpt, "option", 1,
            &index) != TCL_OK) {
          return TCL_ERROR;
      }
      switch (index) {
          /* Ignore foundry, family, weight, slant, charset. */
          case FONT_SIZE: {
            if (Tcl_GetIntFromObj(interp, value, &n) != TCL_OK) {
                goto Error;
            }
            newSize = n;
            break;
          }

          case FONT_UNDERLINE: {
            if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
                goto Error;
            }
            faPtr->underline = n;
            break;
          }

          case FONT_OVERSTRIKE: {
            if (Tcl_GetBooleanFromObj(interp, value, &n) != TCL_OK) {
                goto Error;
            }
            faPtr->overstrike = n;
            break;
          }

          case FONT_POINTADJUST: {
            if (Tcl_GetDoubleFromObj(interp, value, &d) != TCL_OK) {
                goto Error;
            }
            newAdjust = d;
            break;
          }
      }
    }

    if (newSize != faPtr->pointsize || newAdjust != faPtr->pointAdjust) {
      if (newSize < 0) {
          aP = -((double)(-newSize) * newAdjust);
      } else {
          aP = (double)(newSize) * newAdjust;
      }
      if (aP < 0) {
          int tmp = (int)(-aP + 0.5);
          asciiFaPtr->pointsize = -tmp;
      } else {
          asciiFaPtr->pointsize = (int)(aP + 0.5);
      }
      faPtr->pointAdjust = newAdjust;
      faPtr->pointsize = kanjiFaPtr->pointsize = newSize;
    }
    Tk_FreeFont((Tk_Font)ascii);
    Tk_FreeFont((Tk_Font)kanji);
    return TCL_OK;
    Error:
    Tk_FreeFont((Tk_Font)ascii);
    Tk_FreeFont((Tk_Font)kanji);
    return TCL_ERROR;
}
#endif /* KANJI */

/*
 *---------------------------------------------------------------------------
 *
 * GetAttributeInfoObj --
 *
 *    Return information about the font attributes as a Tcl list.
 *
 * Results:
 *    The return value is TCL_OK if the objPtr was non-NULL and
 *    specified a valid font attribute, TCL_ERROR otherwise.  If TCL_OK
 *    is returned, the interp's result object is modified to hold a
 *    description of either the current value of a single option, or a
 *    list of all options and their current values for the given font
 *    attributes.  If TCL_ERROR is returned, the interp's result is
 *    set to an error message describing that the objPtr did not refer
 *    to a valid option.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

static int
GetAttributeInfoObj(interp, faPtr, objPtr)
    Tcl_Interp *interp;             /* Interp to hold result. */
    CONST TkFontAttributes *faPtr;  /* The font attributes to inspect. */
    Tcl_Obj *objPtr;                /* If non-NULL, indicates the single
                               * option whose value is to be
                               * returned. Otherwise
                               * information is returned for
                               * all options. */
{
    int i, index, start, end, num;
    char *str;
    Tcl_Obj *newPtr;

#ifdef KANJI
    char compoundName[1024];
    char pointAdjustBuf[32];
#endif /* KANJI */

    start = 0;
    end = FONT_NUMFIELDS;
    if (objPtr != NULL) {
      if (Tcl_GetIndexFromObj(interp, objPtr, fontOpt, "option", 1,
            &index) != TCL_OK) {
          return TCL_ERROR;
      }
      start = index;
      end = index + 1;
    }

    for (i = start; i < end; i++) {
      str = NULL;
      num = 0;                /* Needed only to prevent compiler
                               * warning. */
      switch (i) {
#ifdef KANJI
          case FONT_FOUNDRY:
            str = faPtr->foundry;
            if (str == NULL) {
                str = "";
            }
            break;

#endif /* KANJI */            
          case FONT_FAMILY:
            str = faPtr->family;
            if (str == NULL) {
                str = "";
            }
            break;

          case FONT_SIZE:
            num = faPtr->pointsize;
#ifdef KANJI
            if (num < 0) {
                Tk_Window tkwin = Tk_MainWindow(interp);
                num = TkpConvertPixelToPoint(tkwin, -num);
            }
#endif /* KANJI */
            break;

          case FONT_WEIGHT:
            str = TkFindStateString(weightMap, faPtr->weight);
            break;
      
          case FONT_SLANT:
            str = TkFindStateString(slantMap, faPtr->slant);
            break;

          case FONT_UNDERLINE:
            num = faPtr->underline;
            break;

          case FONT_OVERSTRIKE:
            num = faPtr->overstrike;
            break;

#ifdef KANJI
          case FONT_CHARSET:
            str = faPtr->charset;
            if (str == NULL) {
                str = "";
            }
            break;

          case FONT_POINTADJUST:
            if (faPtr->fontType == TK_FONT_COMPOUND) {
                double pa = faPtr->pointAdjust;
                sprintf(pointAdjustBuf, "%f", pa);
                str = pointAdjustBuf;
            } else {
                str = NULL;
            }
            break;

          case FONT_COMPOUND:
            if (faPtr->fontType == TK_FONT_COMPOUND) {
                sprintf(compoundName, "{%s} {%s}",
                      faPtr->asciiFontName, faPtr->kanjiFontName);
                str = compoundName;
            } else {
                str = "";
            }
            break;

#endif /* KANJI */
      }
      if (objPtr == NULL) {
          Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
                Tcl_NewStringObj(fontOpt[i], -1));
          if (str != NULL) {
            newPtr = Tcl_NewStringObj(str, -1);
          } else {
            newPtr = Tcl_NewIntObj(num);
          }
          Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
                newPtr);
      } else {
          if (str != NULL) {
            Tcl_SetStringObj(Tcl_GetObjResult(interp), str, -1);
          } else {
            Tcl_SetIntObj(Tcl_GetObjResult(interp), num);
          }
      }
    }
    return TCL_OK;
}

/*
 *---------------------------------------------------------------------------
 *
 * ParseFontNameObj --
 *
 *    Converts a object into a set of font attributes that can be used
 *    to construct a font.
 *
 *    The string rep of the object can be one of the following forms:
 *          XLFD (see X documentation)
 *          "Family [size [style] [style ...]]"
 *          "-option value [-option value ...]"
 *
 * Results:
 *    The return value is TCL_ERROR if the object was syntactically
 *    invalid.  In that case an error message is left in interp's
 *    result object.  Otherwise, fills the font attribute buffer with
 *    the values parsed from the string and returns TCL_OK;
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

static int
ParseFontNameObj(interp, tkwin, objPtr, faPtr)
    Tcl_Interp *interp;       /* Interp for error return. */
    Tk_Window tkwin;          /* For display on which font is used. */
    Tcl_Obj *objPtr;          /* Parseable font description object. */
    TkFontAttributes *faPtr;  /* Font attributes structure whose fields
                         * are to be modified.  Structure must already
                         * be properly initialized. */
{
    char *dash;
    int objc, result, i, n;
    Tcl_Obj **objv;
    TkXLFDAttributes xa;
    char *string;
    
    string = Tcl_GetStringFromObj(objPtr, NULL);
    if (*string == '-') {
      /*
       * This may be an XLFD or an "-option value" string.
       *
       * If the string begins with "-*" or a "-foundry-family-*" pattern,
       * then consider it an XLFD.  
       */

      if (string[1] == '*') {
          goto xlfd;
      }
      dash = strchr(string + 1, '-');
      if ((dash != NULL) && (!isspace(UCHAR(dash[-1])))) {
          goto xlfd;
      }

      if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
          return TCL_ERROR;
      }

      return ConfigAttributesObj(interp, tkwin, objc, objv, faPtr);
    }
    
    if (*string == '*') {
      /*
       * This appears to be an XLFD.
       */

      xlfd:
      xa.fa = *faPtr;
      result = TkParseXLFD(string, &xa);
      if (result == TCL_OK) {
          *faPtr = xa.fa;
          return result;
      }
    }

    /*
     * Wasn't an XLFD or "-option value" string.  Try it as a
     * "font size style" list.
     */

    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
      return TCL_ERROR;
    }
    if (objc < 1) {
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "font \"", string,
            "\" doesn't exist", (char *) NULL);
      return TCL_ERROR;
    }

    faPtr->family = Tk_GetUid(Tcl_GetStringFromObj(objv[0], NULL));
    if (objc > 1) {
      if (Tcl_GetIntFromObj(interp, objv[1], &n) != TCL_OK) {
          return TCL_ERROR;
      }
      faPtr->pointsize = n;
    }

    i = 2;
    if (objc == 3) {
      if (Tcl_ListObjGetElements(interp, objv[2], &objc, &objv) != TCL_OK) {
          return TCL_ERROR;
      }
      i = 0;
    }
    for ( ; i < objc; i++) {
      string = Tcl_GetStringFromObj(objv[i], NULL);
      n = TkFindStateNum(NULL, NULL, weightMap, string);
      if (n != TK_FW_UNKNOWN) {
          faPtr->weight = n;
          continue;
      }
      n = TkFindStateNum(NULL, NULL, slantMap, string);
      if (n != TK_FS_UNKNOWN) {
          faPtr->slant = n;
          continue;
      }
      n = TkFindStateNum(NULL, NULL, underlineMap, string);
      if (n != 0) {
          faPtr->underline = n;
          continue;
      }
      n = TkFindStateNum(NULL, NULL, overstrikeMap, string);
      if (n != 0) {
          faPtr->overstrike = n;
          continue;
      }

      /*
       * Unknown style.
       */

      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
            "unknown font style \"", string, "\"",
            (char *) NULL);
      return TCL_ERROR;
    }
    return TCL_OK;
}

#ifdef CHECK_XTT
Tk_Uid
NormalizeXLFD(string)
    Tk_Uid string;
{
    /*
     * Checking BOGUS XLFD. On XFree86 + X-TT(TrueType rasterizer enhancement for XFree86),
     * returned value of XGetAtomName() for XGetFontProperty(XA_FONT) is NOT valid.
     */
    int i = 0;
    char *str = string;
    while (*str != 0) {
      if (*str == '-') i++;
      str++;
    }

    if (i > XLFD_NUMFIELDS) {
      /*
       * Convert the last "--" to "-". 
       */
      CONST char *found = NULL;
      str = string;
      while ((str = strstr(str, "--")) != NULL) {
          found = str;
          str++;
      }
      if (found != NULL) {
          int bLen = found - string;
          int oLen = strlen(string);
          char fixedXLFD[1024];
          Tk_Uid ret;
          memcpy((VOID *)fixedXLFD, (VOID *)string, (unsigned int)bLen);
          memcpy((VOID *)(fixedXLFD + bLen), (VOID *)(string + bLen + 1),
               (unsigned int)(oLen - bLen - 1));
          fixedXLFD[oLen] = 0;
          ret = Tk_GetUid(fixedXLFD);
          return ret;
      }
    }
    return string;
}

#endif /* CHECK_XTT */
/*
 *---------------------------------------------------------------------------
 *
 * TkParseXLFD --
 *
 *    Break up a fully specified XLFD into a set of font attributes.
 *
 * Results:
 *    Return value is TCL_ERROR if string was not a fully specified XLFD.
 *    Otherwise, fills font attribute buffer with the values parsed
 *    from the XLFD and returns TCL_OK.  
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

int
TkParseXLFD(string, xaPtr)
    CONST char *string;       /* Parseable font description string. */
    TkXLFDAttributes *xaPtr;  /* XLFD attributes structure whose fields
                         * are to be modified.  Structure must already
                         * be properly initialized. */
{
    char *src;
    CONST char *str;
    int i, j;
    char *field[XLFD_NUMFIELDS + 2];
    Tcl_DString ds;
#ifdef CHECK_XTT
    Tk_Uid fixedXLFD = NULL;
    Tk_Uid tmp = NULL;
#endif /* CHECK_XTT */

    memset(field, '\0', sizeof(field));

#ifdef CHECK_XTT
    tmp = Tk_GetUid(string);
    fixedXLFD = NormalizeXLFD((char *)string);
    if (fixedXLFD != tmp) {
      string = fixedXLFD;
    }
#endif /* CHECK_XTT */
    str = string;
    if (*str == '-') {
      str++;
    }

    Tcl_DStringInit(&ds);
    Tcl_DStringAppend(&ds, (char *) str, -1);
    src = Tcl_DStringValue(&ds);

    field[0] = src;
    for (i = 0; *src != '\0'; src++) {
      if (isupper(UCHAR(*src))) {
          *src = tolower(UCHAR(*src));
      }
      if (*src == '-') {
          i++;
          if (i > XLFD_NUMFIELDS) {
            break;
          }
          *src = '\0';
          field[i] = src + 1;
      }
    }

    /*
     * An XLFD of the form -adobe-times-medium-r-*-12-*-* is pretty common, 
     * but it is (strictly) malformed, because the first * is eliding both
     * the Setwidth and the Addstyle fields.  If the Addstyle field is a
     * number, then assume the above incorrect form was used and shift all
     * the rest of the fields up by one, so the number gets interpreted
     * as a pixelsize.  This fix is so that we don't get a million reports
     * that "it works under X, but gives a syntax error under Windows".
     */

    if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) {
      if (atoi(field[XLFD_ADD_STYLE]) != 0) {
          for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) {
            field[j + 1] = field[j];
          }
          field[XLFD_ADD_STYLE] = NULL;
          i++;
      }
    }

    /*
     * Bail if we don't have enough of the fields (up to pointsize).
     */

    if (i < XLFD_FAMILY) {
      Tcl_DStringFree(&ds);
      return TCL_ERROR;
    }

    if (FieldSpecified(field[XLFD_FOUNDRY])) {
      xaPtr->foundry = Tk_GetUid(field[XLFD_FOUNDRY]);
#ifdef KANJI
      xaPtr->fa.foundry = xaPtr->foundry;
#endif /* KANJI */
    }

    if (FieldSpecified(field[XLFD_FAMILY])) {
      xaPtr->fa.family = Tk_GetUid(field[XLFD_FAMILY]);
    }
    if (FieldSpecified(field[XLFD_WEIGHT])) {
      xaPtr->fa.weight = TkFindStateNum(NULL, NULL, xlfdWeightMap,
            field[XLFD_WEIGHT]);
    }
    if (FieldSpecified(field[XLFD_SLANT])) {
      xaPtr->slant = TkFindStateNum(NULL, NULL, xlfdSlantMap,
            field[XLFD_SLANT]);
      if (xaPtr->slant == TK_FS_ROMAN) {
          xaPtr->fa.slant = TK_FS_ROMAN;
      } else {
          xaPtr->fa.slant = TK_FS_ITALIC;
      }
    }
    if (FieldSpecified(field[XLFD_SETWIDTH])) {
      xaPtr->setwidth = TkFindStateNum(NULL, NULL, xlfdSetwidthMap,
            field[XLFD_SETWIDTH]);
#ifdef KANJI
      xaPtr->fa.setwidth = xaPtr->setwidth;
#endif /* KANJI */
    }

    /* XLFD_ADD_STYLE ignored. */

    /*
     * Pointsize in tenths of a point, but treat it as tenths of a pixel.
     */

    if (FieldSpecified(field[XLFD_POINT_SIZE])) {
      if (field[XLFD_POINT_SIZE][0] == '[') {
          /*
           * Some X fonts have the point size specified as follows:
           *
           *          [ N1 N2 N3 N4 ]
           *
           * where N1 is the point size (in points, not decipoints!), and
           * N2, N3, and N4 are some additional numbers that I don't know
           * the purpose of, so I ignore them.
           */

          xaPtr->fa.pointsize = atoi(field[XLFD_POINT_SIZE] + 1);
      } else if (Tcl_GetInt(NULL, field[XLFD_POINT_SIZE],
            &xaPtr->fa.pointsize) == TCL_OK) {
          xaPtr->fa.pointsize /= 10;
      } else {
          return TCL_ERROR;
      }
    }

    /*
     * Pixel height of font.  If specified, overrides pointsize.  */

    if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
      if (field[XLFD_PIXEL_SIZE][0] == '[') {
          /*
           * Some X fonts have the pixel size specified as follows:
           *
           *          [ N1 N2 N3 N4 ]
           *
           * where N1 is the pixel size, and where N2, N3, and N4 
           * are some additional numbers that I don't know
           * the purpose of, so I ignore them.
           */
          xaPtr->fa.pointsize = atoi(field[XLFD_PIXEL_SIZE] + 1);
      } else if (Tcl_GetInt(NULL, field[XLFD_PIXEL_SIZE],
            &xaPtr->fa.pointsize) != TCL_OK) {
          return TCL_ERROR;
      }
#ifdef KANJI
      /* Well, looks like specfying pixel size is stronger than
       * specifying point size. I want to keep the size of the font
       * as POINT SIZE. To notice that point size was overriden by
       * pixel size, invert the sign. And, convert it to accurate
       * point size when I can use Tk_Display(tkwin).*/
      xaPtr->fa.pointsize = -(xaPtr->fa.pointsize);
#endif /* KANJI */
    }

#ifdef KANJI
    if (FieldSpecified(field[XLFD_RESOLUTION_X])) {
      if (Tcl_GetInt(NULL, field[XLFD_RESOLUTION_X], &(xaPtr->resX)) != TCL_OK) {
          xaPtr->resX = 0;
      }
    }

    if (FieldSpecified(field[XLFD_RESOLUTION_Y])) {
      if (Tcl_GetInt(NULL, field[XLFD_RESOLUTION_Y], &(xaPtr->resY)) != TCL_OK) {
          xaPtr->resY = 0;
      }
    }
#else

    xaPtr->fa.pointsize = -xaPtr->fa.pointsize;

    /* XLFD_RESOLUTION_X ignored. */

    /* XLFD_RESOLUTION_Y ignored. */
#endif /* KANJI */

    /* XLFD_SPACING ignored. */

    /* XLFD_AVERAGE_WIDTH ignored. */

    if (FieldSpecified(field[XLFD_REGISTRY])) {
      xaPtr->charset = TkFindStateNum(NULL, NULL, xlfdCharsetMap,
            field[XLFD_REGISTRY]);
#ifdef KANJI
      xaPtr->fa.charset = Tk_GetUid(field[XLFD_REGISTRY]);
      if (!strncasecmp(xaPtr->fa.charset, "jisx0208", 8)) {
          xaPtr->fa.fontType = TK_FONT_2BYTES;
      } else if (!strncasecmp(xaPtr->fa.charset, "gb2312", 6)) {
          xaPtr->fa.fontType = TK_FONT_2BYTES;
      } else if (!strncasecmp(xaPtr->fa.charset, "ksc5601", 7)) {
          xaPtr->fa.fontType = TK_FONT_2BYTES;
      } else if (!strncasecmp(xaPtr->fa.charset, "jisx0201", 8)) {
          xaPtr->fa.fontType = TK_FONT_GENERIC;
      } else if (!strncasecmp(xaPtr->fa.charset, "iso8859", 7)) {
          xaPtr->fa.fontType = TK_FONT_GENERIC;
      } else {
          /* Need further check by platform specific method.  To
           * use this font, we must know whether the font is for
           * 2 byte charset or not. */
          xaPtr->fa.fontType = TK_FONT_OTHER;
      }
#endif /* KANJI */
    }
    if (FieldSpecified(field[XLFD_ENCODING])) {
      xaPtr->encoding = atoi(field[XLFD_ENCODING]);
    }

    Tcl_DStringFree(&ds);
    return TCL_OK;
}

/*
 *---------------------------------------------------------------------------
 *
 * FieldSpecified --
 *
 *    Helper function for TkParseXLFD().  Determines if a field in the
 *    XLFD was set to a non-null, non-don't-care value.
 *
 * Results:
 *    The return value is 0 if the field in the XLFD was not set and
 *    should be ignored, non-zero otherwise.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

static int
FieldSpecified(field)
    CONST char *field;  /* The field of the XLFD to check.  Strictly
                   * speaking, only when the string is "*" does it mean
                   * don't-care.  However, an unspecified or question
                   * mark is also interpreted as don't-care. */
{
    char ch;

    if (field == NULL) {
      return 0;
    }
    ch = field[0];
    return (ch != '*' && ch != '?');
}

/*
 *---------------------------------------------------------------------------
 *
 * NewChunk --
 *
 *    Helper function for Tk_ComputeTextLayout().  Encapsulates a
 *    measured set of characters in a chunk that can be quickly
 *    drawn.
 *
 * Results:
 *    A pointer to the new chunk in the text layout.
 *
 * Side effects:
 *    The text layout is reallocated to hold more chunks as necessary.
 *
 *    Currently, Tk_ComputeTextLayout() stores contiguous ranges of
 *    "normal" characters in a chunk, along with individual tab
 *    and newline chars in their own chunks.  All characters in the
 *    text layout are accounted for.
 *
 *---------------------------------------------------------------------------
 */
static LayoutChunk *
NewChunk(layoutPtrPtr, maxPtr, start, numChars, curX, newX, y)
    TextLayout **layoutPtrPtr;
    int *maxPtr;
    CONST char *start;
    int numChars;
    int curX;
    int newX;
    int y;
{
    TextLayout *layoutPtr;
    LayoutChunk *chunkPtr;
    int maxChunks;
    size_t s;
    
    layoutPtr = *layoutPtrPtr;
    maxChunks = *maxPtr;
    if (layoutPtr->numChunks == maxChunks) {
      maxChunks *= 2;
      s = sizeof(TextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk));
      layoutPtr = (TextLayout *) ckrealloc((char *) layoutPtr, s);

      *layoutPtrPtr = layoutPtr;
      *maxPtr = maxChunks;
    }
    chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
    chunkPtr->start           = start;
    chunkPtr->numChars        = numChars;
    chunkPtr->numDisplayChars = numChars;
    chunkPtr->x               = curX;
    chunkPtr->y               = y;
    chunkPtr->totalWidth      = newX - curX;
    chunkPtr->displayWidth    = newX - curX;
    layoutPtr->numChunks++;

    return chunkPtr;
}
#ifdef KANJI

int
Tk_GetFontType(tkfont)
     Tk_Font tkfont;
{
    return Tk_FontType(tkfont);
}

int
Tk_GetCompoundDescendant(tkfont, ascii, kanji)
     Tk_Font tkfont;
     Tk_Font *ascii;
     Tk_Font *kanji;
{
    if (ascii == NULL || kanji == NULL) return TCL_ERROR;
    if (Tk_FontType(tkfont) == TK_FONT_COMPOUND) {
      *ascii = (Tk_Font)((TkFont *)tkfont)->asciiFontPtr;
      *kanji = (Tk_Font)((TkFont *)tkfont)->kanjiFontPtr;
      return TCL_OK;
    }
    return TCL_ERROR;
}
      
#ifdef COMPOUND_DEBUG
static char debugMsgBuf[4096];
static char *
DumpCompFont(msg, child, parent)
     char *msg;
     Tk_Font child;
     Tk_Font parent;
{
    if (parent != NULL) {
      sprintf(debugMsgBuf, "DumpFont:%-10.10s: parent=0x%08lx(0x%08lx='%s' 0x%08lx='%s') child=0x%08lx",
            msg,
            parent,
            ((TkFont *)parent)->asciiFontPtr, 
            ((TkFont *)parent)->fa.asciiFontName, 
            ((TkFont *)parent)->kanjiFontPtr,
            ((TkFont *)parent)->fa.kanjiFontName,
            child);
    } else {
      sprintf(debugMsgBuf, "DumpFont:%-10.10s: child=0x%08lx", msg, child);
    }
    return debugMsgBuf;
}
#endif /* COMPOUND_DEBUG */

/*
 *---------------------------------------------------------------------------
 *
 * AllocCompoundParent --
 *
 *    Allocate a linked list structure for a compound font reference.
 *
 * Results:
 *    Return the pointer of the structure.
 *
 * Side effects:
 *    Memory allocated.
 *
 *---------------------------------------------------------------------------
 */

static CompoundParent *
AllocCompoundParent()
{
    CompoundParent *ret = (CompoundParent *)ckalloc(sizeof(CompoundParent));
    memset((VOID *)ret, 0, sizeof(CompoundParent));
    return ret;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_AddCompoundParent --
 *
 *    Add a reference of the compound font to a member of the
 *    compound font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Linked list structure is allocated if needed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_AddCompoundParent(child, tkfont)
     Tk_Font child;
     Tk_Font tkfont;
{
    CompoundParent *top = &(((TkFont *)child)->parent);
    CompoundParent *cur = top;
    CompoundParent *prev = cur;

    if (top->parent == NULL) {
      top->next = NULL;
      top->parent = tkfont;
      goto done;
    }
    while (cur != NULL) {
      if (cur->parent == tkfont) return;
      prev = cur;
      cur = cur->next;
    }
    if (cur == NULL) {
      cur = AllocCompoundParent();
    }
    prev->next = cur;
    cur->next = NULL;
    cur->parent = tkfont;
    done:
#ifdef COMPOUND_DEBUG
    fprintf(stderr, "%s.\n", DumpCompFont("Add", child, tkfont));
#endif /* COMPOUND_DEBUG */
    return;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_DeleteCompoundParent --
 *
 *    Delete a reference of the compound font from a member of the
 *    compound font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Linked list structure is freed if needed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_DeleteCompoundParent(child, tkfont)
     Tk_Font child;
     Tk_Font tkfont;
{
#ifdef COMPOUND_DEBUG
    char *msg = DumpCompFont("Delete", child, tkfont);
#endif /* COMPOUND_DEBUG */
    CompoundParent *top = &(((TkFont *)child)->parent);
    CompoundParent *cur = top;
    CompoundParent *prev = cur;
    while (cur != NULL) {
        if (cur->parent == tkfont) break;
      prev = cur;
      cur = cur->next;
    }
    if (cur == NULL) {
#ifdef COMPOUND_DEBUG
      fprintf(stderr, "%s ... failed.\n", msg);
#endif /* COMPOUND_DEBUG */
      return;
    }

    if (cur == top) {
      if (cur->next != NULL) {
          cur = cur->next;
          top->parent = cur->parent;
          top->next = cur->next;
          ckfree((char *)cur);
      } else {
          top->parent = NULL;
          top->next = NULL;
      }
      goto done;
    } else if (cur->next == NULL) {
      prev->next = NULL;
    } else {
      prev->next = cur->next;
    }

    ckfree((char *)cur);
    done:
#ifdef COMPOUND_DEBUG
    fprintf(stderr, "%s.\n", msg);
#endif /* COMPOUND_DEBUG */
    return;
}

/*
 *---------------------------------------------------------------------------
 *
 * FreeAllCP
 *
 *    This function is called recursively to free a linked list.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Linked list structure is freed if needed.
 *
 *---------------------------------------------------------------------------
 */

static void
FreeAllCP(top, child)
     CompoundParent *top;
     Tk_Font child;
{
    if (top->next != NULL) {
      FreeAllCP(top->next);
    } else {
#ifdef COMPOUND_DEBUG
      fprintf(stderr, "%s.\n", DumpCompFont("Free(R)", child, top->parent));
#endif /* COMPOUND_DEBUG */
      ckfree((char *)top);
      return;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FreeCompoundParent --
 *
 *    Free all reference of the compound font from a member of the
 *    compound font. This function must be called from
 *    TkpDeleteFont() when Deleting the font that type is not
 *    TK_FONT_COMPOUND.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Linked list structure is freed if needed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_FreeCompoundParent(child)
     Tk_Font child;
{
    CompoundParent *top = &(((TkFont *)child)->parent);
#ifdef COMPOUND_DEBUG
    char buf[4096];
    sprintf(buf, "%s", DumpCompFont("Free", child, NULL));
    fprintf(stderr, "%s ... called.\n", buf);
#endif /* COMPOUND_DEBUG */
    if (top->next != NULL) {
      FreeAllCP(top->next);
    }
    top->next = NULL;
    top->parent = NULL;
#ifdef COMPOUND_DEBUG
    fprintf(stderr, "%s ... done.\n", buf);
#endif /* COMPOUND_DEBUG */
    return;
}  

/*
 *---------------------------------------------------------------------------
 *
 * Tk_GetCompoundParentList --
 *
 *    Get a list of the compound fonts that use the fonts.
 *
 * Results:
 *    Return an array of Tk_Font. *numPtr has number of elements.
 *
 * Side effects:
 *    Memory allocated if needed.
 *
 *---------------------------------------------------------------------------
 */

Tk_Font *
Tk_GetCompoundParentList(child, numPtr)
     Tk_Font child;
     int *numPtr;
{
    int n = 0;
    CompoundParent *top = &(((TkFont *)child)->parent);
    Tk_Font *pPtr = (Tk_Font *)ckalloc(sizeof(Tk_Font));
    while (top->parent != NULL) {
      pPtr[n] = (Tk_Font)(top->parent);
      n++;
      pPtr = (Tk_Font *)ckrealloc((VOID *)pPtr, sizeof(Tk_Font) * (n + 1));
      top = top->next;
      if (top == NULL) break;
    }
    *numPtr = n;
    if (n == 0) {
      ckfree((char *)pPtr);
      return NULL;
    }
    return pPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_FreeCompoundParentList --
 *
 *    Free the array allocated by previous call of the
 *    Tk_GetCompoundParentList().
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Memory freed.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_FreeCompoundParentList(parentList)
     Tk_Font *parentList;
{
    ckfree((char *)parentList);
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UpdateCompoundParent --
 *
 *    Update the compound font that use this font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Compound font is updated. To do this, TkpUpdateCompoundFont()
 *    will be called.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_UpdateCompoundParent(tkwin, child)
     Tk_Window tkwin;
     Tk_Font child;
{
    int n = 0;
    Tk_Font *parents = Tk_GetCompoundParentList(child, &n);
#ifdef COMPOUND_DEBUG
    char buf[4096];
    sprintf(buf, "%s", DumpCompFont("Update", child, NULL));
    fprintf(stderr, "%s ... called.\n", buf);
#endif /* COMPOUND_DEBUG */
    if (n > 0) {
      int i;
      for (i = 0; i < n; i++) {
#ifdef COMPOUND_DEBUG
          fprintf(stderr, "%s.\n", DumpCompFont("Update(R)", child, parents[i]));
#endif /* COMPOUND_DEBUG */
          TkpUpdateCompoundFont((TkFont *)parents[i], NULL);
      }
      Tk_FreeCompoundParentList(parents);
    }
#ifdef COMPOUND_DEBUG
    fprintf(stderr, "%s ... done.\n", buf);
#endif /* COMPOUND_DEBGU */
}

/*
 *---------------------------------------------------------------------------
 *
 * Tk_UpdateCompoundDescendants --
 *
 *    Update the font that are used by this font.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Descendant fonts is updated.
 *
 *---------------------------------------------------------------------------
 */

void
Tk_UpdateCompoundDescendants(tkwin, parent)
     Tk_Window tkwin;
     Tk_Font parent;
{
    TkFont *ascii;
    TkFont *kanji;

    Tk_GetCompoundDescendant(parent, (Tk_Font *)&ascii, (Tk_Font *)&kanji);
    TkpGetFontFromAttributes(ascii, tkwin, &ascii->fa);
    TkpGetFontFromAttributes(kanji, tkwin, &kanji->fa);
}
#endif /* KANJI */

Generated by  Doxygen 1.6.0   Back to index