Blob Blame History Raw
/* "$Id: utf8Wrap.c 11243 2016-02-27 15:14:42Z AlbrechtS $"
 *
 * Author: Jean-Marc Lienher ( http://oksid.ch )
 * Copyright 2000-2003 by O'ksi'D.
 *
 * This library is free software. Distribution and use rights are outlined in
 * the file "COPYING" which should have been included with this file.  If this
 * file is missing or damaged, see the license at:
 *
 *     http://www.fltk.org/COPYING.php
 *
 * Please report all bugs and problems on the following page:
 *
 *     http://www.fltk.org/str.php
 */

/*
 * X11 UTF-8 text drawing functions.
 */
#if !defined(WIN32) && !defined(__APPLE__)

#include "../Xutf8.h"
#include <X11/Xlib.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* External auto generated functions : */
#include "ucs2fontmap.c"
/*
 * extern int ucs2fontmap(char *s, unsigned int ucs, int enc);
 * extern int encoding_number(const char *enc);
 * extern const char *encoding_name(int num);
 */

/* The ARM header files have a bug by not taking into account that ARM cpu
 * likes pacing to 4 bytes. This little trick defines our own version of
 * XChar2b which does not have this problem
 */

#if defined(__GNUC__) && defined(__arm__) && !defined(__ARM_EABI__)
typedef struct {
  unsigned char byte1;
  unsigned char byte2;
}
__attribute__ ((packed))
Fl_XChar2b;
#else
#define Fl_XChar2b XChar2b
#endif


/*********************************************************************/
/** extract a list of font from the base font name list             **/
/*********************************************************************/
static int
get_font_list(
	const char	*base_font_name_list,
	char 		***flist) {
  const char *ptr;
  const char *p;
  int nb;
  int nb_name;

  ptr = base_font_name_list;
  p = NULL;
  nb = 0;
  nb_name = 1;

  while (*ptr) {
    if (*ptr == ',') nb_name++;
    ptr++;
  }

  *flist = (char **) malloc(sizeof(char*) * nb_name);
  ptr = base_font_name_list;

  while (*ptr) {
    int l = 0, i = 0;

    while(isspace((int)(unsigned char)*ptr)) ptr++;
    p = ptr;
    while (*ptr && *ptr != ',') { ptr++; l++; }
    if (l > 2) {
      (*flist)[nb] = (char*) malloc((unsigned)l + 2);
      while (p != ptr) { ((*flist)[nb])[i] = *p; i++; p++; }
      (*flist)[nb][i] = '\0';
      nb++;
    }
    if (*ptr) ptr++;
  }
  if (nb < 1) {
    free(*flist);
    *flist = (char**)NULL;
  }
  return nb;
}

/*********************************************************************/
/** get the font name used as encoding for "fontspecific" encoding  **/
/** (mainly used for adobe-symbol and adobe-zapfdingbats)	    **/
/*********************************************************************/
static int
font_spec_enc(char *font) {
  int ret;
  char *enc;
  char *end;

  enc = font;
  while (*enc != '-') enc++;
  enc++;
  while (*enc != '-') enc++;
  enc++;
  end = enc;
  while (*end != '-') end++;
  *end = '\0';

  ret = encoding_number(enc);
  *end = '-';

  return ret;
}


/*********************************************************************/
/** get the sub range of a iso10646-1 font			    **/
/*********************************************************************/
static void
get_range(const char	*enc,
	  int 		*min,
	  int		*max) {

  const char *ptr = enc;
  const char *ptr1;

  if (!enc) return;

  while (*ptr && *ptr != '-') ptr++;
  if (!*ptr) return;
  while (*ptr && *ptr != '[') ptr++;
  if (!*ptr) return;
  *min = 0xFFFF;
  *max = 0;
  while (*ptr && *ptr != ']') {
    int val;
    ptr++;
    ptr1 = ptr;
    while (*ptr && *ptr != ']' && *ptr != ' ' && *ptr != '_') ptr++;
    val = strtol(ptr1, NULL, 0);
    if (val < *min) *min = val;
    if (val > *max) *max = val;
  }
}

/*********************************************************************/
/** get the internal encoding number of each fonts 		    **/
/*********************************************************************/
static int *
get_encodings(char	**font_name_list,
	      int 	*ranges,
	      int 	nb_font) {

  int *font_encoding_list;
  int i;
  i = 0;

  font_encoding_list = (int *) malloc(sizeof(int) * nb_font);
  while (i < nb_font) {
    char *ptr;
    int ec;
    ptr = font_name_list[i];
    ec = 0;
    font_encoding_list[i] = -1;
    ranges[i * 2] = 0;
    ranges[i * 2 + 1] = 0xFFFF;

    if (ptr && strstr(ptr, "fontspecific")) {
      font_encoding_list[i] = font_spec_enc(ptr);
      ptr = NULL;
    }
    while (ptr && *ptr) {
      if (*ptr == '-') {
	ec++;
	if (ec == 13) {
	  font_encoding_list[i] = encoding_number(ptr + 1);
	  if (font_encoding_list[i] == 0) {
	    get_range(ptr + 1,
		      ranges + i * 2,
		      ranges + i * 2 + 1);
	  }
	  break;
	}
      }
      ptr++;
    }
    if (font_encoding_list[i] < 0) font_encoding_list[i] = 1;
    i++;
  }
  return font_encoding_list;
}

/*********************************************************************/
/** find the first font which matches the name and load it.	    **/
/*********************************************************************/
static XFontStruct *
find_best_font(Display  *dpy,
	       char     **name) {

  char **list;
  int cnt;
  XFontStruct *s;

  list = XListFonts(dpy, *name, 1, &cnt);
  if (cnt && list) {
    free(*name);
    *name = strdup(list[0]);
    s = XLoadQueryFont(dpy, *name);
    XFreeFontNames(list);
    return s;
  }
  return NULL;
}

/*********************************************************************/
/** load all fonts 						    **/
/*********************************************************************/
static void
load_fonts(Display 	   *dpy,
	   XUtf8FontStruct *font_set) {

  int i = 0;

  font_set->fonts = (XFontStruct**) malloc(sizeof(XFontStruct*) *
                                           font_set->nb_font);

  font_set->ranges = (int*) malloc(sizeof(int) *
                                   font_set->nb_font * 2);

  font_set->descent = 0;
  font_set->ascent = 0;
  font_set->fid = 0;

  while (i < font_set->nb_font) {
    XFontStruct *fnt;

    fnt = font_set->fonts[i] =
      find_best_font(dpy, &(font_set->font_name_list[i]));
    if (fnt) {
      font_set->fid = fnt->fid;
      if (fnt->ascent > font_set->ascent) {
	font_set->ascent = fnt->ascent;
      }
      if (fnt->descent > font_set->descent) {
	font_set->descent = fnt->descent;
      }
    } else {
      free(font_set->font_name_list[i]);
      font_set->font_name_list[i] = NULL;
    }
    i++;
  }

  font_set->encodings =
    get_encodings(font_set->font_name_list,
      font_set->ranges, font_set->nb_font);

  /* unload fonts with same encoding */
  for (i = 0; i < font_set->nb_font; i++) {
    if (font_set->font_name_list[i]) {
      int j;
      for (j = 0; j < i; j++) {
	if (font_set->font_name_list[j] &&
	    font_set->encodings[j] ==
	    font_set->encodings[i] &&
	    font_set->ranges[2*j] ==
	    font_set->ranges[2*i] &&
	    font_set->ranges[(2*j)+1] &&
	    font_set->ranges[(2*i)+1]) {
	  XFreeFont(dpy, font_set->fonts[i]);
	  free(font_set->font_name_list[i]);
	  font_set->font_name_list[i] = NULL;
	  font_set->fonts[i] = 0;
	}
      }
    }
  }
}

/*********************************************************************/
/** Creates an array of XFontStruct acording to the comma separated **/
/** list of fonts. XLoad all fonts.				    **/
/*********************************************************************/
XUtf8FontStruct *
XCreateUtf8FontStruct(Display    *dpy,
		      const char *base_font_name_list) {

  XUtf8FontStruct *font_set;

  font_set = (XUtf8FontStruct*)malloc(sizeof(XUtf8FontStruct));

  if (!font_set) {
    return NULL;
  }

  font_set->nb_font = get_font_list(base_font_name_list,
				    &font_set->font_name_list);

  if (font_set->nb_font < 1) {
    free(font_set);
    return NULL;
  }

  load_fonts(dpy, font_set);

  return font_set;
}


/*****************************************************************************/
/** draw a Right To Left UTF-8 string using multiple fonts as needed.	    **/
/*****************************************************************************/
void
XUtf8DrawRtlString(Display 		*display,
		   Drawable 		d,
		   XUtf8FontStruct 	*font_set,
		   GC 			gc,
		   int 			x,
		   int 			y,
		   const char		*string,
		   int 			num_bytes) {

  int 		*encodings;	/* encodings array */
  XFontStruct 	**fonts;	/* fonts array */
  Fl_XChar2b 	buf[128];	/* drawing buffer */
  Fl_XChar2b	*ptr;		/* pointer to the drawing buffer */
  int 		fnum;		/* index of the current font in the fonts array*/
  int 		i;		/* current byte in the XChar2b buffer */
  int 		first;		/* first valid font index */
  int 		last_fnum;	/* font index of the previous char */
  int 		nb_font;	/* quantity of fonts in the font array */
  char 		glyph[2];	/* byte1 and byte2 value of the UTF-8 char */
  int		*ranges;	/* sub range of iso10646 */

  nb_font = font_set->nb_font;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return;
  }

  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  i = 0;
  fnum = 0;
  ptr = buf + 128;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return;
  }

  first = fnum;
  last_fnum = fnum;

  while (num_bytes > 0) {
    int 	 ulen;   /* byte length of the UTF-8 char */
    unsigned int ucs;    /* Unicode value of the UTF-8 char */
    unsigned int no_spc; /* Spacing char equivalent of a non-spacing char */

    if (i > 120) {
      /*** draw the buffer **/
      XSetFont(display, gc, fonts[fnum]->fid);
      x -= XTextWidth16(fonts[fnum], ptr, i);
      XDrawString16(display, d, gc, x, y, ptr, i);
      i = 0;
      ptr = buf + 128;
    }

    ulen = XFastConvertUtf8ToUcs((const unsigned char*)string, num_bytes, &ucs);

    if (ulen < 1) ulen = 1;

    no_spc = XUtf8IsNonSpacing(ucs);
    if (no_spc) ucs = no_spc;

    /*
     * find the first encoding which can be used to
     * draw the glyph
     */
    fnum = first;
    while (fnum < nb_font) {
      if (fonts[fnum] && ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
	if (encodings[fnum] != 0 ||
	    ((int)ucs >= ranges[fnum * 2] && (int)ucs <= ranges[fnum * 2 + 1])) {
	  break;
	}
      }
      fnum++;
    }
    if (fnum == nb_font) {
      /* the char is not valid in all encodings ->
       * draw it using the first font :-(
       */
      fnum = first;
      ucs2fontmap(glyph, '?', encodings[fnum]);
    }

    if (last_fnum != fnum || no_spc) {
      XSetFont(display, gc, fonts[last_fnum]->fid);
      x -= XTextWidth16(fonts[last_fnum], ptr, i);
      XDrawString16(display, d, gc, x, y, ptr, i);
      i = 0;
      ptr = buf + 127;
      (*ptr).byte1 = glyph[0];
      (*ptr).byte2 = glyph[1];
      if (no_spc) {
	x += XTextWidth16(fonts[fnum], ptr, 1);
      }
    } else {
      ptr--;
      (*ptr).byte1 = glyph[0];
      (*ptr).byte2 = glyph[1];
    }
    last_fnum = fnum;
    i++;
    string += ulen;
    num_bytes -= ulen;
  }

  if (i < 1) return;

  XSetFont(display, gc, fonts[fnum]->fid);
  x -= XTextWidth16(fonts[last_fnum], ptr, i);
  XDrawString16(display, d, gc, x, y, ptr, i);
}


/*****************************************************************************/
/** draw an UTF-8 string using multiple fonts as needed.		    **/
/*****************************************************************************/
void
XUtf8DrawString(Display 	*display,
	        Drawable 	d,
                XUtf8FontStruct *font_set,
	        GC 		gc,
	        int 		x,
	        int 		y,
	        const char	*string,
                int 		num_bytes) {

  int 		*encodings; /* encodings array */
  XFontStruct 	**fonts;    /* fonts array */
  Fl_XChar2b 	buf[128];   /* drawing buffer */
  int 		fnum;       /* index of the current font in the fonts array*/
  int 		i;          /* current byte in the XChar2b buffer */
  int 		first;      /* first valid font index */
  int 		last_fnum;  /* font index of the previous char */
  int 		nb_font;    /* quantity of fonts in the font array */
  char 		glyph[2];   /* byte1 and byte2 value of the UTF-8 char */
  int		*ranges;    /* sub range of iso10646 */

  nb_font = font_set->nb_font;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return;
  }
  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  i = 0;
  fnum = 0;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return;
  }

  first = fnum;
  last_fnum = fnum;

  while (num_bytes > 0) {
    int 	 ulen;   /* byte length of the UTF-8 char */
    unsigned int ucs;    /* Unicode value of the UTF-8 char */
    unsigned int no_spc; /* Spacing char equivalent of a non-spacing char */

    if (i > 120) {
      /*** draw the buffer **/
      XSetFont(display, gc, fonts[fnum]->fid);
      XDrawString16(display, d, gc, x, y, buf, i);
      x += XTextWidth16(fonts[fnum], buf, i);
      i = 0;
    }

    ulen = XFastConvertUtf8ToUcs((const unsigned char*)string, num_bytes, &ucs);

    if (ulen < 1) ulen = 1;

    no_spc = XUtf8IsNonSpacing(ucs);
    if (no_spc) ucs = no_spc;

    /*
     * find the first encoding which can be used to
     * draw the glyph
     */
    fnum = first;
    while (fnum < nb_font) {
      if (fonts[fnum] && ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
	if (encodings[fnum] != 0 ||
	    ((int)ucs >= ranges[fnum * 2] &&
	     (int)ucs <= ranges[fnum * 2 + 1])) {
	  break;
	}
      }
      fnum++;
    }
    if (fnum == nb_font) {
      /* the char is not valid in all encodings ->
       * draw it using the first font :-(
       */
      fnum = first;
      ucs2fontmap(glyph, '?', encodings[fnum]);
    }

    if (last_fnum != fnum || no_spc) {
      XSetFont(display, gc, fonts[last_fnum]->fid);
      XDrawString16(display, d, gc, x, y, buf, i);
      x += XTextWidth16(fonts[last_fnum], buf, i);
      i = 0;
      (*buf).byte1 = glyph[0];
      (*buf).byte2 = glyph[1];
      if (no_spc) {
	x -= XTextWidth16(fonts[fnum], buf, 1);
      }
    } else {
      (*(buf + i)).byte1 = glyph[0];
      (*(buf + i)).byte2 = glyph[1];
    }
    last_fnum = fnum;
    i++;
    string += ulen;
    num_bytes -= ulen;
  }

  XSetFont(display, gc, fonts[fnum]->fid);
  XDrawString16(display, d, gc, x, y, buf, i);
}


/*****************************************************************************/
/** Measure the inked extents of a UTF-8 string using multiple fonts as     **/
/** needed. Tries to mirror the behaviour of the draw function              **/
/** XUtf8DrawString() as closely as possible to get consistent sizes.       **/
/*****************************************************************************/
void
XUtf8_measure_extents(
        Display         	*display,
        Drawable        	d,
        XUtf8FontStruct  *font_set,
        GC              	gc,
        int             	*xx,     /* x-offset from origin */
        int             	*yy,     /* y-offset from origin */
        int             	*ww,     /* overall inked width  */
        int             	*hh,     /* maximum inked height */
        const char      	*string, /* text to measure */
        int             	num_bytes) {
  int 		*encodings; /* encodings array */
  XFontStruct 	**fonts;    /* fonts array */
  Fl_XChar2b 	buf[128];   /* drawing buffer */
  int 		fnum;       /* index of the current font in the fonts array*/
  int 		i;          /* current byte in the XChar2b buffer */
  int 		first;      /* first valid font index */
  int 		last_fnum;  /* font index of the previous char */
  int 		nb_font;    /* quantity of fonts in the font array */
  char 		glyph[2];   /* byte1 and byte2 value of the UTF-8 char */
  int		*ranges;    /* sub range of iso10646 */

  int wd = 0; /* accumulates the width of the text */
  int ht = 0; /* used to find max height in text */
  int hs;     /* "height sum" of current text segment */
  int yt = 0x7FFFFFFF; /* used to find bounding rectangle delta-y */
  /* int res; */ /* result from calling XTextExtents16() - we should test this is OK! */
  /* FC: the man does not specify error codes for it, but X will generate X errors like BadGC or BadFont. */

  XCharStruct sizes;
  int dir_ret = 0;
  int fnt_asc = 0;
  int fnt_dsc = 0;

  nb_font = font_set->nb_font;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return;
  }
  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  i = 0;
  fnum = 0;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return;
  }

  first = fnum;
  last_fnum = fnum;

  while (num_bytes > 0) {
    int 	 ulen;   /* byte length of the UTF-8 char */
    unsigned int ucs;    /* Unicode value of the UTF-8 char */
    unsigned int no_spc; /* Spacing char equivalent of a non-spacing char */

    if (i > 120) {
      /*** draw the buffer **/
      XSetFont(display, gc, fonts[fnum]->fid);
      /* res = */ XTextExtents16(fonts[fnum], buf, i, &dir_ret, &fnt_asc, &fnt_dsc, &sizes);
      /* recover the dimensions - should verify that res == 0 first! */
      wd += sizes.width; /* accumulate the width */
      hs = sizes.ascent + sizes.descent; /* total height */
      if(hs > ht) ht = hs; /* new height exceeds previous height */
      if(yt > (-sizes.ascent)) yt = -sizes.ascent; /* delta y offset */
      i = 0;
    }

    ulen = XFastConvertUtf8ToUcs((const unsigned char*)string, num_bytes, &ucs);

    if (ulen < 1) ulen = 1;

    no_spc = XUtf8IsNonSpacing(ucs);
    if (no_spc) ucs = no_spc;

    /*
     * find the first encoding which can be used to
     * draw the glyph
     */
    fnum = first;
    while (fnum < nb_font) {
      if (fonts[fnum] && ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
	if (encodings[fnum] != 0 ||
	    ((int)ucs >= ranges[fnum * 2] &&
	     (int)ucs <= ranges[fnum * 2 + 1])) {
	  break;
	}
      }
      fnum++;
    }
    if (fnum == nb_font) {
      /* the char is not valid in all encodings ->
       * draw it using the first font :-(
       */
      fnum = first;
      ucs2fontmap(glyph, '?', encodings[fnum]);
    }

    if (last_fnum != fnum || no_spc) {
      XSetFont(display, gc, fonts[last_fnum]->fid);
      /* res = */ XTextExtents16(fonts[last_fnum], buf, i, &dir_ret, &fnt_asc, &fnt_dsc, &sizes);
      /* recover the dimensions - should verify that res == 0 first! */
      wd += sizes.width; /* accumulate the width */
      hs = sizes.ascent + sizes.descent; /* total height */
      if(hs > ht) ht = hs; /* new height exceeds previous height */
      if(yt > (-sizes.ascent)) yt = -sizes.ascent; /* delta y offset */
      i = 0;
      (*buf).byte1 = glyph[0];
      (*buf).byte2 = glyph[1];
      if (no_spc) {
	wd -= XTextWidth16(fonts[fnum], buf, 1);
      }
    } else {
      (*(buf + i)).byte1 = glyph[0];
      (*(buf + i)).byte2 = glyph[1];
    }
    last_fnum = fnum;
    i++;
    string += ulen;
    num_bytes -= ulen;
  }

  XSetFont(display, gc, fonts[fnum]->fid);
  /* res = */ XTextExtents16(fonts[fnum], buf, i, &dir_ret, &fnt_asc, &fnt_dsc, &sizes);
  /* recover the dimensions - should verify that res == 0 first! */
  wd += sizes.width; /* accumulate the width */
  hs = sizes.ascent + sizes.descent; /* total height */
  if(hs > ht) ht = hs; /* new height exceeds previous height */
  if(yt > (-sizes.ascent)) yt = -sizes.ascent; /* delta y offset */
  /* return values */
  *ww = wd; /* width of inked area rectangle */
  *hh = ht; /* max height of inked area rectangle */
  *xx = 0;  /* x-offset from origin to start of inked area - this is wrong! */
  *yy = yt; /* y-offset from origin to start of inked rectangle */
}


/*****************************************************************************/
/** returns the pixel width of a UTF-8 string				    **/
/*****************************************************************************/
int
XUtf8TextWidth(XUtf8FontStruct 	*font_set,
	       const char 	*string,
	       int 		num_bytes) {

  int		x;
  int 		*encodings; /* encodings array */
  XFontStruct 	**fonts;    /* fonts array */
  Fl_XChar2b 	buf[128];   /* drawing buffer */
  int 		fnum;       /* index of the current font in the fonts array*/
  int 		i;          /* current byte in the XChar2b buffer */
  int 		first;      /* first valid font index */
  int 		last_fnum;  /* font index of the previous char */
  int 		nb_font;    /* quantity of fonts in the font array */
  char 		glyph[2];   /* byte1 and byte2 value of the UTF-8 char */
  int		*ranges;    /* sub range of iso10646 */

  nb_font = font_set->nb_font;
  x = 0;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return x;
  }

  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  i = 0;
  fnum = 0;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return x;
  }

  first = fnum;
  last_fnum = fnum;

  while (num_bytes > 0) {
    int 	 ulen;   /* byte length of the UTF-8 char */
    unsigned int ucs;    /* Unicode value of the UTF-8 char */
    unsigned int no_spc; /* Spacing char equivalent of a non-spacing char */

    if (i > 120) {
      /*** measure the buffer **/
      x += XTextWidth16(fonts[fnum], buf, i);
      i = 0;
    }

    ulen = XFastConvertUtf8ToUcs((const unsigned char*)string, num_bytes, &ucs);

    if (ulen < 1) ulen = 1;

    no_spc = XUtf8IsNonSpacing(ucs);
    if (no_spc) {
      ucs = no_spc;
    }

    /*
     * find the first encoding which can be used to
     * draw the glyph
     */
    fnum = first;
    while (fnum < nb_font) {
      if (fonts[fnum] && ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
	if (encodings[fnum] != 0 ||
	    ((int)ucs >= ranges[fnum * 2] &&
	     (int)ucs <= ranges[fnum * 2 + 1])) {
	  break;
	}
      }
      fnum++;
    }
    if (fnum == nb_font) {
      /* the char is not valid in all encodings ->
       * draw it using the first font :-(
       */
      fnum = first;
      ucs2fontmap(glyph, '?', encodings[fnum]);
    }

    if (last_fnum != fnum || no_spc) {
      x += XTextWidth16(fonts[last_fnum], buf, i);
      i = 0;
      (*buf).byte1 = glyph[0];
      (*buf).byte2 = glyph[1];
      if (no_spc) {
	/* go back to draw the non-spacing char over the previous char */
	x -= XTextWidth16(fonts[fnum], buf, 1);
      }
    } else {
      (*(buf + i)).byte1 = glyph[0];
      (*(buf + i)).byte2 = glyph[1];
    }
    last_fnum = fnum;
    i++;
    string += ulen;
    num_bytes -= ulen;
  }

  x += XTextWidth16(fonts[last_fnum], buf, i);

  return x;
}

/*****************************************************************************/
/**  get the X font and glyph ID of a UCS char                              **/
/*****************************************************************************/
int
fl_XGetUtf8FontAndGlyph(XUtf8FontStruct  *font_set,
			unsigned int     ucs,
			XFontStruct      **fnt,
			unsigned short   *id) {

  int             *encodings; /* encodings array */
  XFontStruct     **fonts;    /* fonts array */
  int             fnum;       /* index of the current font in the fonts array*/
  int             first;      /* first valid font index */
  int             nb_font;    /* quantity of fonts in the font array */
  char 		  glyph[2];   /* byte1 and byte2 value of the UTF-8 char */
  int             *ranges;    /* sub range of iso10646 */

  nb_font = font_set->nb_font;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return -1;
  }

  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  fnum = 0;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return -1;
  }

  first = fnum;

  /*
   * find the first encoding which can be used to draw the glyph
   */
  fnum = first;
  while (fnum < nb_font) {
    if (fonts[fnum] && ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
      if (encodings[fnum] != 0 ||
          ((int)ucs >= ranges[fnum * 2] &&
	   (int)ucs <= ranges[fnum * 2 + 1])) {
	break;
      }
    }
    fnum++;
  }
  if (fnum == nb_font) {
    /* the char is not valid in all encodings ->
     * draw it using the first font :-(
     */
    fnum = first;
    ucs2fontmap(glyph, '?', encodings[fnum]);
  }

  *id = ((unsigned char)glyph[0] << 8) | (unsigned char)glyph[1] ;
  *fnt = fonts[fnum];
  return 0;
}

/*****************************************************************************/
/** returns the pixel width of a UCS char				    **/
/*****************************************************************************/
int
XUtf8UcsWidth(XUtf8FontStruct  *font_set,
	      unsigned int     ucs) {

  int		x;
  int 		*encodings; /* encodings array */
  XFontStruct 	**fonts;    /* fonts array */
  Fl_XChar2b 	buf[8];     /* drawing buffer */
  int 		fnum;       /* index of the current font in the fonts array*/
  int 		first;      /* first valid font index */
  int 		nb_font;    /* quantity of fonts in the font array */
  char 		glyph[2];   /* byte1 and byte2 value of the UTF-8 char */
  int		*ranges;    /* sub range of iso10646 */
  unsigned int  no_spc;

  nb_font = font_set->nb_font;
  x = 0;

  if (nb_font < 1) {
    /* there is no font in the font_set :-( */
    return x;
  }

  ranges = font_set->ranges;
  fonts = font_set->fonts;
  encodings = font_set->encodings;
  fnum = 0;

  while(fnum < nb_font && !fonts[fnum]) fnum++;
  if (fnum >= nb_font) {
    /* there is no valid font for the X server */
    return x;
  }

  first = fnum;

  no_spc = XUtf8IsNonSpacing(ucs);
  if (no_spc) ucs = no_spc;

  /*
   * find the first encoding which can be used to
   * draw the glyph
   */
  fnum = first;
  while (fnum < nb_font) {
    if (fonts[fnum] &&
	ucs2fontmap(glyph, ucs, encodings[fnum]) >= 0) {
      if (encodings[fnum] != 0 || ((int)ucs >= ranges[fnum * 2] &&
	  (int)ucs <= ranges[fnum * 2 + 1])) {
	break;
      }
    }
    fnum++;
  }
  if (fnum == nb_font) {
    /* the char is not valid in all encodings ->
     * draw it using the first font :-(
     */
    fnum = first;
    ucs2fontmap(glyph, '?', encodings[fnum]);
  }

  (*buf).byte1 = glyph[0];
  (*buf).byte2 = glyph[1];

  x += XTextWidth16(fonts[fnum], buf, 1);

  return x;
}

/*****************************************************************************/
/** draw an UTF-8 string and clear the background.	 		    **/
/*****************************************************************************/
void
XUtf8DrawImageString(Display         *display,
		     Drawable        d,
		     XUtf8FontStruct *font_set,
		     GC              gc,
		     int             x,
		     int             y,
		     const char      *string,
		     int             num_bytes) {

  /* FIXME: must be improved ! */
  int w;
  int fill_style;
  unsigned long foreground;
  unsigned long background;
  int function;
  XGCValues xgcv;

  w = XUtf8TextWidth(font_set, string, num_bytes);

  XGetGCValues(display, gc,
	       GCFunction|GCForeground|GCBackground|GCFillStyle, &xgcv);

  function = xgcv.function;
  fill_style = xgcv.fill_style;
  foreground = xgcv.foreground;
  background = xgcv.background;

  xgcv.function = GXcopy;
  xgcv.foreground = background;
  xgcv.background = foreground;
  xgcv.fill_style = FillSolid;

  XChangeGC(display, gc,
	    GCFunction|GCForeground|GCBackground|GCFillStyle, &xgcv);

  XFillRectangle(display, d, gc, x, y - font_set->ascent,
	         (unsigned)w, (unsigned)(font_set->ascent + font_set->descent));

  xgcv.function = function;
  xgcv.foreground = foreground;
  xgcv.background = background;
  xgcv.fill_style = fill_style;

  XChangeGC(display, gc,
	    GCFunction|GCForeground|GCBackground|GCFillStyle, &xgcv);

  XUtf8DrawString(display, d, font_set, gc, x, y, string, num_bytes);
}

/*****************************************************************************/
/** free the XFontSet and others things created by XCreateUtf8FontSet       **/
/*****************************************************************************/
void
XFreeUtf8FontStruct(Display 	    *dpy,
		    XUtf8FontStruct *font_set) {

  int i;
  i = 0;
  while (i < font_set->nb_font) {
    if (font_set->fonts[i]) {
	XFreeFont(dpy, font_set->fonts[i]);
	free(font_set->font_name_list[i]);
    }
    i++;
  }
  free(font_set->ranges);
  free(font_set->font_name_list);
  free(font_set->fonts);
  free(font_set->encodings);
  free(font_set);
}

#endif /* X11 only */

/*
 *  End of "$Id: utf8Wrap.c 11243 2016-02-27 15:14:42Z AlbrechtS $".
 */