text: /* Copyright (C) 2000-2010 by George Williams */
  /*
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
  
   * Redistributions of source code must retain the above copyright notice, this
   * list of conditions and the following disclaimer.
  
   * Redistributions in binary form must reproduce the above copyright notice,
   * this list of conditions and the following disclaimer in the documentation
   * and/or other materials provided with the distribution.
  
   * The name of the author may not be used to endorse or promote products
   * derived from this software without specific prior written permission.
  
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
   * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #include "fontforgevw.h"
  #include <string.h>
  #include <ustring.h>
  #include <utype.h>
  #include <math.h>
  
  int use_freetype_to_rasterize_fv = 1;
  char *BDFFoundry=NULL;
  
  struct std_bdf_props StandardProps[] = {
  	{ "FONT", prt_atom, true },
  	{ "COMMENT", prt_string, false },
  	{ "FOUNDRY", prt_string|prt_property, true },
  	{ "FAMILY_NAME", prt_string|prt_property, true },
  	{ "WEIGHT_NAME", prt_string|prt_property, true },
  	{ "SLANT", prt_string|prt_property, true },
  	{ "SETWIDTH_NAME", prt_string|prt_property, true },
  	{ "ADD_STYLE_NAME", prt_string|prt_property, true },
  	{ "PIXEL_SIZE", prt_int|prt_property, true },
  	{ "POINT_SIZE", prt_int|prt_property, true },
  	{ "RESOLUTION_X", prt_uint|prt_property, true },
  	{ "RESOLUTION_Y", prt_uint|prt_property, true },
  	{ "SPACING", prt_string|prt_property, true },
  	{ "AVERAGE_WIDTH", prt_int|prt_property, true },
  	{ "CHARSET_REGISTRY", prt_string|prt_property, true },
  	{ "CHARSET_ENCODING", prt_string|prt_property, true },
  	{ "FONTNAME_REGISTRY", prt_string|prt_property, true },
  	{ "CHARSET_COLLECTIONS", prt_string|prt_property, true },
  	{ "BITS_PER_PIXEL", prt_int|prt_property, true },
  	{ "FONT_NAME", prt_string|prt_property, true },
  	{ "FACE_NAME", prt_string|prt_property, true },
  	{ "COPYRIGHT", prt_string|prt_property, true },
  	{ "FONT_VERSION", prt_string|prt_property, true },
  	{ "FONT_ASCENT", prt_int|prt_property, true },
  	{ "FONT_DESCENT", prt_int|prt_property, true },
  	{ "UNDERLINE_POSITION", prt_int|prt_property, true },
  	{ "UNDERLINE_THICKNESS", prt_int|prt_property, true },
  	{ "X_HEIGHT", prt_int|prt_property, true },
  	{ "CAP_HEIGHT", prt_int|prt_property, true },
  	{ "DEFAULT_CHAR", prt_uint|prt_property, true },
  	{ "RAW_ASCENT", prt_int|prt_property, true },
  	{ "RAW_DESCENT", prt_int|prt_property, true },
  	{ "ITALIC_ANGLE", prt_int|prt_property, true },
  	{ "NORM_SPACE", prt_int|prt_property, true },
  	{ "QUAD_WIDTH", prt_int|prt_property, true },	/* Depreciated */
  	{ "RESOLUTION", prt_uint|prt_property, true },	/* Depreciated */
  	{ "RELATIVE_SETWIDTH", prt_uint|prt_property, true },
  	{ "RELATIVE_WEIGHT", prt_uint|prt_property, true },
  	{ "SUPERSCRIPT_X", prt_int|prt_property, true },
  	{ "SUPERSCRIPT_Y", prt_int|prt_property, true },
  	{ "SUPERSCRIPT_SIZE", prt_int|prt_property, true },
  	{ "SUBSCRIPT_X", prt_int|prt_property, true },
  	{ "SUBSCRIPT_Y", prt_int|prt_property, true },
  	{ "SUBSCRIPT_SIZE", prt_int|prt_property, true },
  	{ "FIGURE_WIDTH", prt_int|prt_property, true },
  	{ "AVG_LOWERCASE_WIDTH", prt_int|prt_property, true },
  	{ "AVG_UPPERCASE_WIDTH", prt_int|prt_property, true },
  #ifdef FONTFORGE_CONFIG_BDF_GLYPH_RANGES
  	{ "_XFREE86_GLYPH_RANGES", prt_string|prt_property, true },
  #endif
  	{ "WEIGHT", prt_uint|prt_property, false },
  	{ "DESTINATION", prt_uint|prt_property, false },
  	{ "MIN_SPACE", prt_int|prt_property, false },
  	{ "MAX_SPACE", prt_int|prt_property, false },
  	{ "END_SPACE", prt_int|prt_property, false },
  	{ "SMALL_CAP_SIZE", prt_int|prt_property, false },
  	{ "STRIKEOUT_ASCENT", prt_int|prt_property, false },
  	{ "STRIKEOUT_DESCENT", prt_int|prt_property, false },
  	{ "NOTICE", prt_string|prt_property, false },
  	{ "FONT_TYPE", prt_string|prt_property, false },
  	{ "RASTERIZER_NAME", prt_string|prt_property, false },
  	{ "RASTERIZER_VERSION", prt_string|prt_property, false },
  	{ "AXIS_NAMES", prt_string|prt_property, false },
  	{ "AXIS_LIMITS", prt_string|prt_property, false },
  	{ "AXIS_TYPES", prt_string|prt_property, false },
  	NULL
  };
  
  int IsUnsignedBDFKey(char *key) {
      /* X says that some properties are signed and some are unsigned */
      /* neither bdf nor pcf supports this, but David (of freetype) */
      /* thought the sfnt BDF table should include it */
      /* Since these are X11 atom names, case is significant */
      int i;
  
      for ( i=0; StandardProps[i].name!=NULL; ++i )
  	if ( strcmp(key,StandardProps[i].name)==0 )
  return(( StandardProps[i].type&~prt_property)==prt_uint );
  
  return( false );
  }
  
  char *BdfPropHasString(BDFFont *font,const char *key, char *def ) {
      int i;
  
      for ( i=0; i<font->prop_cnt; ++i ) if ( strcmp(font->props[i].name,key)==0 ) {
  	switch ( font->props[i].type&~prt_property ) {
  	  case prt_string:
  	    if ( font->props[i].u.str!=NULL )		/* Can be NULL when creating & defaulting a prop */
  return( font->props[i].u.str );
  	  break;
  	  case prt_atom:
  	    if ( font->props[i].u.atom!=NULL )		/* Can be NULL when creating & defaulting a prop */
  return( font->props[i].u.atom );
  	  break;
  	}
      }
  return( def );
  }
  
  int BdfPropHasInt(BDFFont *font,const char *key, int def ) {
      int i;
  
      for ( i=0; i<font->prop_cnt; ++i ) if ( strcmp(font->props[i].name,key)==0 ) {
  	switch ( font->props[i].type&~prt_property ) {
  	  case prt_int: case prt_uint:
  return( font->props[i].u.val );
  	  break;
  	}
      }
  return( def );
  }
  
  static void BDFPropAddString(BDFFont *bdf,char *keyword,char *value,char *match_key) {
      int i;
  
      if ( match_key!=NULL && strcmp(keyword,match_key)!=0 )
  return;
  
      for ( i=0; i<bdf->prop_cnt; ++i )
  	if ( strcmp(keyword,bdf->props[i].name)==0 ) {
  	    if ( (bdf->props[i].type&~prt_property)==prt_string ||
  		    (bdf->props[i].type&~prt_property)==prt_atom )
  		free( bdf->props[i].u.str );
      break;
  	}
      if ( i==bdf->prop_cnt ) {
  	if ( i>=bdf->prop_max )
  	    bdf->props = grealloc(bdf->props,(bdf->prop_max+=10)*sizeof(BDFProperties));
  	++bdf->prop_cnt;
  	bdf->props[i].name = copy(keyword);
      }
      if ( strcmp(keyword,"FONT")==0 )
  	bdf->props[i].type = prt_atom;
      else if ( strcmp(keyword,"COMMENT")==0 )
  	bdf->props[i].type = prt_string;
      else
  	bdf->props[i].type = prt_string | prt_property;
      bdf->props[i].u.str = copy(value);
  }
  
  static void BDFPropAddInt(BDFFont *bdf,char *keyword,int value,char *match_key) {
      int i;
  
      if ( match_key!=NULL && strcmp(keyword,match_key)!=0 )
  return;
  
      for ( i=0; i<bdf->prop_cnt; ++i )
  	if ( strcmp(keyword,bdf->props[i].name)==0 ) {
  	    if ( (bdf->props[i].type&~prt_property)==prt_string ||
  		    (bdf->props[i].type&~prt_property)==prt_atom )
  		free( bdf->props[i].u.str );
      break;
  	}
      if ( i==bdf->prop_cnt ) {
  	if ( i>=bdf->prop_max )
  	    bdf->props = grealloc(bdf->props,(bdf->prop_max+=10)*sizeof(BDFProperties));
  	++bdf->prop_cnt;
  	bdf->props[i].name = copy(keyword);
      }
      if ( IsUnsignedBDFKey(keyword) )
  	bdf->props[i].type = prt_uint | prt_property;
      else
  	bdf->props[i].type = prt_int | prt_property;
      bdf->props[i].u.val = value;
  }
  
  static char *AllSame(BDFFont *font,int *avg,int *cnt) {
      int c=0, a=0, common= -1;
      int i;
      BDFChar *bdfc;
      int cell = -1;
  
      /* Return 'P' if a proporitional font */
      /* Return 'C' if an X11 "cell" font (all glyphs within (0,width)x(ascent,desent) & monospace ) */
      /* Return 'M' if monospace (and not a cell) */
      for ( i=0; i<font->glyphcnt; ++i ) {
  	if ( (bdfc = font->glyphs[i])!=NULL && !IsntBDFChar(bdfc)) {
  	    ++c;
  	    a += bdfc->width;
  	    if ( common==-1 ) common = bdfc->width; else if ( common!=bdfc->width ) { common = -2; cell = 0; }
  	    if ( cell ) {
  		if ( bdfc->xmin<0 || bdfc->xmax>common || bdfc->ymax>font->ascent ||
  			-bdfc->ymin>font->descent )
  		    cell = 0;
  		else
  		    cell = 1;
  	    }
  	}
      }
      if ( c==0 ) *avg=0; else *avg = (10*a)/c;
      *cnt = c;
  return(common==-2 ? "P" : cell ? "C" : "M" );
  }
  
  static void BDFPropAppendString(BDFFont *bdf,char *keyword,char *value) {
      int i = bdf->prop_cnt;
  
      if ( i>=bdf->prop_max )
  	bdf->props = grealloc(bdf->props,(bdf->prop_max+=10)*sizeof(BDFProperties));
      ++bdf->prop_cnt;
      bdf->props[i].name = copy(keyword);
      if ( strcmp(keyword,"COMMENT")==0 )
  	bdf->props[i].type = prt_string;
      else if ( strcmp(keyword,"FONT")==0 )
  	bdf->props[i].type = prt_atom;
      else
  	bdf->props[i].type = prt_string | prt_property;
      bdf->props[i].u.str = copy(value);
  }
  
  #ifdef FONTFORGE_CONFIG_BDF_GLYPH_RANGES
  static void BDFPropClearKey(BDFFont *bdf,char *keyword) {
      int i, j;
  
      for ( i=j=0; i<bdf->prop_cnt; ++i ) {
  	if ( strcmp(bdf->props[i].name,keyword)==0 ) {
  	    free(bdf->props[i].name);
  	    if ( (bdf->props[i].type&~prt_property)==prt_string ||
  		    (bdf->props[i].type&~prt_property)==prt_atom )
  		free(bdf->props[i].u.str );
  	} else if ( i!=j ) {
  	    bdf->props[j++] = bdf->props[i];
  	} else
  	    ++j;
      }
  }
  #endif		/* FONTFORGE_CONFIG_BDF_GLYPH_RANGES */
  
  static int BDFPropReplace(BDFFont *bdf,const char *key, const char *value) {
      int i;
      char *pt;
  
      for ( i=0; i<bdf->prop_cnt; ++i ) if ( strcmp(bdf->props[i].name,key)==0 ) {
  	switch ( bdf->props[i].type&~prt_property ) {
  	  case prt_string:
  	  case prt_atom:
  	    free( bdf->props[i].u.atom );
  	  break;
  	}
  	if ( (bdf->props[i].type&~prt_property)!=prt_atom )
  	    bdf->props[i].type = (bdf->props[i].type&prt_property)|prt_string;
  	pt = strchr(value,'\n');
  	if ( pt==NULL )
  	    bdf->props[i].u.str = copy(value);
  	else
  	    bdf->props[i].u.str = copyn(value,pt-value);
  return( true );
      }
  return( false );
  }
  
  static void def_Charset_Col(SplineFont *sf,EncMap *map, char *buffer) {
      uint32 codepages[2];
      /* A buffer with 250 bytes should be more than big enough */
  
      OS2FigureCodePages(sf,codepages);
      buffer[0] = '\0';
      if ( codepages[1]&(1U<<31) )
  	strcat(buffer , "ASCII " );
      if ( (codepages[1]&(1U<<30)) )
  	strcat(buffer , "ISOLatin1Encoding " );
      if ( (codepages[0]&2) )
  	strcat(buffer , "ISO8859-2 " );
      if ( (codepages[0]&4) )
  	strcat(buffer , "ISO8859-5 " );
      if ( (codepages[0]&8) )
  	strcat(buffer , "ISO8859-7 " );
      if ( (codepages[0]&0x10) )
  	strcat(buffer , "ISO8859-9 " );
      if ( (codepages[0]&0x20) )
  	strcat(buffer , "ISO8859-8 " );
      if ( (codepages[0]&0x40) )
  	strcat(buffer , "ISO8859-6 " );
      if ( (codepages[0]&0x80) )
  	strcat(buffer , "ISO8859-4 " );
      if ( (codepages[0]&0x10000)  )
  	strcat(buffer , "ISO8859-11 " );
      if ( (codepages[0]&0x20000) && (map->enc->is_unicodebmp || map->enc->is_unicodefull ))
  	strcat(buffer , "JISX0208.1997 " );
      if ( (codepages[0]&0x40000) && (map->enc->is_unicodebmp || map->enc->is_unicodefull ) )
  	strcat(buffer , "GB2312.1980 " );
      if ( (codepages[0]&0x80000) && (map->enc->is_unicodebmp || map->enc->is_unicodefull ))
  	strcat(buffer , "KSC5601.1992 " );
      if ( (codepages[0]&0x100000) && (map->enc->is_unicodebmp || map->enc->is_unicodefull ))
  	strcat(buffer , "BIG5 " );
      if ( (codepages[0]&0x80000000) )
  	strcat(buffer , "Symbol " );
  
      strcat(buffer , EncodingName(map->enc) );
  }
  
  void SFReplaceEncodingBDFProps(SplineFont *sf,EncMap *map) {
      BDFFont *bdf;
      char buffer[250];
      char reg[100], enc[40], *pt, *bpt;
  
      def_Charset_Col(sf,map, buffer);
      def_Charset_Enc(map,reg,enc);
  
      for ( bdf=sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
  	BDFPropReplace(bdf,"CHARSET_REGISTRY", reg);
  	BDFPropReplace(bdf,"CHARSET_ENCODING", enc);
  	BDFPropReplace(bdf,"CHARSET_COLLECTIONS",buffer);
  	if ( (bpt = BdfPropHasString(bdf,"FONT", NULL))!=NULL ) {
  	    strncpy(buffer,bpt,sizeof(buffer)); buffer[sizeof(buffer)-1] = '\0';
  	    pt = strrchr(buffer,'-');
  	    if ( pt!=NULL ) for ( --pt; pt>buffer && *pt!='-'; --pt );
  	    if ( pt>buffer ) {
  		sprintf( pt+1, "%s-%s", reg, enc);
  		BDFPropReplace(bdf,"FONT",buffer);
  	    }
  	}
      }
  }
  
  void SFReplaceFontnameBDFProps(SplineFont *sf) {
      BDFFont *bdf;
      char *bpt, *pt;
      char buffer2[300];
  
      for ( bdf=sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
  	BDFPropReplace(bdf,"FONT_NAME", sf->fontname);
  	BDFPropReplace(bdf,"FAMILY_NAME", sf->familyname);
  	BDFPropReplace(bdf,"FULL_NAME", sf->fullname);
  	BDFPropReplace(bdf,"COPYRIGHT", sf->copyright);
  	if ( (bpt = BdfPropHasString(bdf,"FONT", NULL))!=NULL ) {
  	    if ( bpt[0]=='-' ) {
  		for ( pt = bpt+1; *pt && *pt!='-'; ++pt );
  		if ( *pt=='-' ) {
  		    *pt = '\0';
  		    strcpy(buffer2,bpt);
  		    strcat(buffer2,"-");
  		    strcat(buffer2,sf->familyname);
  		    for ( ++pt ; *pt && *pt!='-'; ++pt );
  		    strcat(buffer2,pt);
  		    BDFPropReplace(bdf,"FONT",buffer2);
  		}
  	    }
  	}
      }
  }
  
  BDFProperties *BdfPropsCopy(BDFProperties *props, int cnt ) {
      BDFProperties *ret;
      int i;
  
      if ( cnt==0 )
  return( NULL );
      ret = galloc(cnt*sizeof(BDFProperties));
      memcpy(ret,props,cnt*sizeof(BDFProperties));
      for ( i=0; i<cnt; ++i ) {
  	ret[i].name = copy(ret[i].name);
  	if ( (ret[i].type&~prt_property)==prt_string || (ret[i].type&~prt_property)==prt_atom )
  	    ret[i].u.str = copy(ret[i].u.str);
      }
  return( ret );
  }
  
  void def_Charset_Enc(EncMap *map,char *reg,char *enc) {
      char *pt;
  
      if ( map->enc->is_custom || map->enc->is_original ) {
  	strcpy( reg, "FontSpecific" );
  	strcpy( enc, "0");
      } else if ( (pt = strstr(map->enc->enc_name,"8859"))!=NULL ) {
  	strcpy( reg, "ISO8859" );
  	pt += 4;
  	if ( !isdigit(*pt)) ++pt;
  	strcpy(enc, pt );
      } else if ( map->enc->is_unicodebmp || map->enc->is_unicodefull ) {
  	strcpy( reg, "ISO10646" );
  	strcpy( enc, "1" );
      } else if ( strstr(map->enc->enc_name, "5601")!=NULL ) {
  	strcpy( reg, "KSC5601.1992" );
  	strcpy( enc, "3" );
      } else if ( strstr(map->enc->enc_name, "2312")!=NULL ) {
  	strcpy( reg, "GB2312.1980" );
  	strcpy( enc, "0" );
      } else if ( strstrmatch(map->enc->enc_name, "JISX0208")!=NULL ) {
  	strcpy( reg, "JISX0208.1997" );
  	strcpy( enc, "0" );
      } else {
  	strcpy( reg, EncodingName(map->enc) );
  	pt = strchr( reg,'-');
  	if ( pt==NULL )
  	    strcpy( enc, "0" );
  	else {
  	    strcpy( enc, pt+1 );
  	    *pt = '\0';
  	}
      }
  }
  
  static void decomposename(BDFFont *font, char *fontname, char *family_name, char *weight_name,
  	char *slant, char *stylename, char *squeeze, char *sffamily, char *sfweight ) {
      char *ital, *bold, *style, *compress;
      char ich='\0', bch='\0', sch='\0', cch='\0';
      char *pt;
  
      if ( *fontname=='-' ) {
  	sscanf(fontname,"-%*[^-]-%[^-]-%[^-]-%[^-]-%[^-]-%[^-]",
  		family_name,weight_name,slant,squeeze,stylename);
      } else {
  	/* Assume it's a fontographer type name */
  	strcpy(slant,"R");
  	strcpy(squeeze,"Normal");
  /* Sigh. I don't use "Italic" and "Oblique" because urw truncates these to 4 */
  /*  characters each. */
  	if (( ital = strstrmatch(fontname,"Ital"))!=NULL )
  	    strcpy(slant,"I");
  	else if (( ital = strstrmatch(fontname,"Kurs"))!=NULL )
  	    strcpy(slant,"I");
  	else if (( ital = strstr(fontname,"Obli"))!=NULL )
  	    strcpy(slant,"O");
  	else if (( ital = strstr(fontname,"Slanted"))!=NULL )
  	    strcpy(slant,"O");
  /* The XLFD spec also supports RI and RO (reverse skewed italic/oblique) */
      
  	if (( bold = strstr(fontname,"Bold"))==NULL &&
  		( bold = strstr(fontname,"Ligh"))==NULL &&
  		( bold = strstr(fontname,"Demi"))==NULL &&
  		( bold = strstr(fontname,"Blac"))==NULL &&
  		( bold = strstr(fontname,"Roma"))==NULL &&
  		( bold = strstr(fontname,"Book"))==NULL &&
  		( bold = strstr(fontname,"Regu"))==NULL &&
  		( bold = strstr(fontname,"Medi"))==NULL );	/* Again, URW */
      
  	if (( style = strstr(fontname,"Sans"))==NULL &&
  		(compress = strstr(fontname,"SmallCaps"))==NULL );
  	if ((compress = strstr(fontname,"Extended"))==NULL &&
  		(compress = strstr(fontname,"Condensed"))==NULL );
      
  	strcpy(weight_name,"Medium");
  	*stylename='\0';
  	if ( bold!=NULL ) { bch = *bold; *bold = '\0'; }
  	if ( ital!=NULL ) { ich = *ital; *ital = '\0'; }
  	if ( style!=NULL ) { sch = *style; *style = '\0'; }
  	if ( compress!=NULL ) { cch = *compress; *compress = '\0'; }
  	strcpy(family_name,fontname);
  	if ( bold!=NULL ) {
  	    *bold = bch; 
  	    strcpy(weight_name,bold);
  	    *bold = '\0';
  	}
  	if ( sfweight!=NULL && *sfweight!='\0' )
  	    strcpy(weight_name,sfweight);
  	if ( style!=NULL ) {
  	    *style = sch;
  	    strcpy(stylename,style);
  	    *style = '\0';
  	}
  	if ( compress!=NULL ) {
  	    *compress = cch;
  	    strcpy(squeeze,compress);
  	}
  	if ( style!=NULL ) *style = sch;
  	if ( bold!=NULL ) *bold = bch; 
  	if ( ital!=NULL ) *ital = ich;
      }
      if ( sffamily!=NULL && *sffamily!='\0' )
  	strcpy(family_name,sffamily);
      while ( (pt=strchr(family_name,'-'))!=NULL )
  	strcpy(pt,pt+1);
  }
  
  static char *getcomponent(char *xlfd,char *pt,int maxlen) {
      char *end = pt+maxlen-1;
  
      while ( *xlfd!='-' && *xlfd!='\0' && pt<end )
  	*pt++ = *xlfd++;
      while ( *xlfd!='-' && *xlfd!='\0' )
  	++xlfd;
      *pt = '\0';
  return( xlfd );
  }
  
  void XLFD_GetComponents(char *xlfd, struct xlfd_components *components) {
      /* can't use sscanf because it fails if %[^-] matches an empty string */
  
      memset(components,0,sizeof(*components));
  
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->foundry,sizeof(components->foundry));
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->family,sizeof(components->family));
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->weight,sizeof(components->weight));
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->slant,sizeof(components->slant));
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->setwidth,sizeof(components->setwidth));
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->add_style,sizeof(components->add_style));	
      if ( *xlfd=='-' )
  	components->pixel_size = strtol(xlfd+1,&xlfd,10);
      if ( *xlfd=='-' )
  	components->point_size = strtol(xlfd+1,&xlfd,10);
      if ( *xlfd=='-' )
  	components->res_x = strtol(xlfd+1,&xlfd,10);
      if ( *xlfd=='-' )
  	components->res_y = strtol(xlfd+1,&xlfd,10);
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->spacing,sizeof(components->spacing));	
      if ( *xlfd=='-' )
  	components->avg_width = strtol(xlfd+1,&xlfd,10);
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->cs_reg,sizeof(components->cs_reg));	
      if ( *xlfd=='-' )
  	xlfd = getcomponent(xlfd+1,components->cs_enc,sizeof(components->cs_enc));	
  }
  
  void XLFD_CreateComponents(BDFFont *font,EncMap *map, int res, struct xlfd_components *components) {
      int avg, cnt, pnt;
      char *mono;
      char family_name[80], weight_name[60], slant[10], stylename[40], squeeze[40];
      char *sffn = *font->sf->fontname ? font->sf->fontname : "Untitled";
      char reg[100], enc[40];
      int old_res;
  
      mono = AllSame(font,&avg,&cnt);
      old_res = BdfPropHasInt(font,"RESOLUTION_X",-1);
      if ( res!=-1 )
  	/* Already set */;
      else if ( old_res>0 )
  	res = old_res;
      else if ( font->res>0 )
  	res = font->res;
      else if ( font->pixelsize==33 || font->pixelsize==28 || font->pixelsize==17 || font->pixelsize==14 )
  	res = 100;
      else
  	res = 75;
  
      pnt = ((font->pixelsize*72+res/2)/res)*10;
      if ( pnt==230 && res==75 )
  	pnt = 240;
  
      decomposename(font, sffn, family_name, weight_name, slant,
  	    stylename, squeeze, font->sf->familyname, font->sf->weight);
      def_Charset_Enc(map,reg,enc);
      strncpy(components->foundry,
  	    BdfPropHasString(font,"FOUNDRY", font->foundry!=NULL?font->foundry:BDFFoundry==NULL?"FontForge":BDFFoundry),sizeof(components->foundry));
      strncpy(components->family,
  	    BdfPropHasString(font,"FAMILY_NAME", family_name),sizeof(components->family));
      strncpy(components->weight,
  	    BdfPropHasString(font,"WEIGHT_NAME", weight_name),sizeof(components->weight));
      strncpy(components->slant,
  	    BdfPropHasString(font,"SLANT", slant),sizeof(components->slant));
      strncpy(components->setwidth,
  	    BdfPropHasString(font,"SETWIDTH_NAME", squeeze),sizeof(components->setwidth));
      strncpy(components->add_style,
  	    BdfPropHasString(font,"ADD_STYLE_NAME", stylename),sizeof(components->add_style));
      components->pixel_size = font->pixelsize;
      components->point_size = res==old_res ? BdfPropHasInt(font,"POINT_SIZE", pnt) : pnt;
      components->res_x = res;
      components->res_y = res;
      strncpy(components->spacing,
  	    BdfPropHasString(font,"SPACING", mono),sizeof(components->spacing));
      components->avg_width = avg;
      strncpy(components->cs_reg,
  	    BdfPropHasString(font,"CHARSET_REGISTRY", reg),sizeof(components->cs_reg));
      strncpy(components->cs_enc,
  	    BdfPropHasString(font,"CHARSET_ENCODING", enc),sizeof(components->cs_enc));
  
      components->foundry[sizeof(components->foundry)-1] = '\0';
      components->family[sizeof(components->family)-1] = '\0';
      components->weight[sizeof(components->weight)-1] = '\0';
      components->slant[sizeof(components->slant)-1] = '\0';
      components->setwidth[sizeof(components->setwidth)-1] = '\0';
      components->add_style[sizeof(components->add_style)-1] = '\0';
      components->spacing[sizeof(components->spacing)-1] = '\0';
      components->cs_reg[sizeof(components->cs_reg)-1] = '\0';
      components->cs_enc[sizeof(components->cs_enc)-1] = '\0';
  
      components->char_cnt = cnt;
  }
  
  void Default_XLFD(BDFFont *bdf,EncMap *map, int res) {
      char buffer[800];
      struct xlfd_components components;
  
      XLFD_CreateComponents(bdf,map, res, &components);
      snprintf( buffer, sizeof(buffer), "-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d-%s-%s",
  	    components.foundry,
  	    components.family,
  	    components.weight,
  	    components.slant,
  	    components.setwidth,
  	    components.add_style,
  	    components.pixel_size,
  	    components.point_size,
  	    components.res_x,
  	    components.res_y,
  	    components.spacing,
  	    components.avg_width,
  	    components.cs_reg,
  	    components.cs_enc);
      BDFPropAddString(bdf,"FONT",buffer,NULL);
  }
  
  static int GenerateGlyphRanges(BDFFont *font) {
  #ifdef FONTFORGE_CONFIG_BDF_GLYPH_RANGES
      char buffer[300], *pt, *end, add[30];
      int i, j, max, cnt=0;
  
      /* I gather that the _XFREE86_GLYPH_RANGES property has been dropped */
      /*  because it was felt that the metrics data would allow users to */
      /*  determine whether a character was in the font or not. I'm not in */
      /*  complete agreement with that argument because there are several */
      /*  zero-width spaces whose presence is not specified by the metrics */
      /*  but it is almost irrelevant whether they are present or not (though */
      /*  guessing wrong would present you with a "DEFAULT_CHAR" rather than */
      /*  nothing) */
  
      if ( map->enc->is_unicodebmp || map->enc->is_unicodefull )
  	max = map->enc->char_cnt;
      else
  return( 0 );
      BDFPropClearKey(font,"_XFREE86_GLYPH_RANGES");
      pt = buffer; end = pt+sizeof(buffer);
      for ( i=0; i<font->glyphcnt && i<max; ++i ) if ( !IsntBDFChar(font->glyphs[i]) ) {
  	for ( j=i+1; j<font->glyphcnt && j<max && !IsntBDFChar(font->glyphs[j]); ++j );
  	--j;
  	if ( j==i )
  	    sprintf( add, "%d ", i );
  	else
  	    sprintf( add, "%d_%d ", i, j );
  	i = j;
  	if ( pt+strlen(add) >= end ) {
  	    pt[-1] = '\0';		/* was a space */
  	    BDFPropAppendString(font,"_XFREE86_GLYPH_RANGES", buffer);
  	    pt = buffer;
  	    ++cnt;
  	}
  	strcpy(pt,add);
  	pt += strlen(pt);
      }
      if ( pt!=buffer ) {
  	pt[-1] = '\0';		/* was a space */
  	BDFPropAppendString(font,"_XFREE86_GLYPH_RANGES", buffer);
  	++cnt;
      }
  return( cnt );
  #else
  return( 0 );
  #endif		/* FONTFORGE_CONFIG_BDF_GLYPH_RANGES */
  }
  
  void Default_Properties(BDFFont *bdf,EncMap *map,char *onlyme) {
      char *xlfd = BdfPropHasString(bdf,"FONT",NULL);
      struct xlfd_components components;
      int x_h= -1, cap_h= -1, def_ch=-1, gid;
  
      if ( (gid=SFFindExistingSlot(bdf->sf,'x',NULL))!=-1 && bdf->glyphs[gid]!=NULL ) {
  	x_h = bdf->glyphs[gid]->ymax;
      }
      if ( 'X'<map->enccount && (gid=map->map['X'])!=-1 && bdf->glyphs[gid]!=NULL ) {
  	cap_h = bdf->glyphs[gid]->ymax;
      }
      def_ch = SFFindNotdef(bdf->sf,-2);
      if ( def_ch!=-1 ) {
  	def_ch = map->map[def_ch];		/* BDF works on the encoding, not on gids, so the default char must be encoded to be useful */
  	if ( def_ch>=map->enc->char_cnt )
  	    def_ch = -1;
      }
  
      if ( xlfd!=NULL )
  	XLFD_GetComponents(xlfd,&components);
      else
  	XLFD_CreateComponents(bdf,map, -1, &components);
      BDFPropAddString(bdf,"FOUNDRY",components.foundry,onlyme);
      BDFPropAddString(bdf,"FAMILY_NAME",components.family,onlyme);
      BDFPropAddString(bdf,"WEIGHT_NAME",components.weight,onlyme);
      BDFPropAddString(bdf,"SLANT",components.slant,onlyme);
      BDFPropAddString(bdf,"SETWIDTH_NAME",components.setwidth,onlyme);
      BDFPropAddString(bdf,"ADD_STYLE_NAME",components.add_style,onlyme);
      BDFPropAddInt(bdf,"PIXEL_SIZE",bdf->pixelsize,onlyme);
      BDFPropAddInt(bdf,"POINT_SIZE",components.point_size,onlyme);
      BDFPropAddInt(bdf,"RESOLUTION_X",components.res_x,onlyme);
      BDFPropAddInt(bdf,"RESOLUTION_Y",components.res_y,onlyme);
      BDFPropAddString(bdf,"SPACING",components.spacing,onlyme);
      BDFPropAddInt(bdf,"AVERAGE_WIDTH",components.avg_width,onlyme);
      BDFPropAddString(bdf,"CHARSET_REGISTRY",components.cs_reg,onlyme);
      BDFPropAddString(bdf,"CHARSET_ENCODING",components.cs_enc,onlyme);
  
      BDFPropAddString(bdf,"FONTNAME_REGISTRY","",onlyme);
      {
  	char buffer[250];
  	def_Charset_Col(bdf->sf,map, buffer);
  	BDFPropAddString(bdf,"CHARSET_COLLECTIONS", buffer,onlyme );
      }
  
      if ( bdf->clut!=NULL )
  	BDFPropAddInt( bdf, "BITS_PER_PIXEL", BDFDepth(bdf),onlyme);
      BDFPropAddString(bdf,"FONT_NAME",bdf->sf->fontname,onlyme);
      BDFPropAddString(bdf,"FACE_NAME",bdf->sf->fullname,onlyme);	/* Used to be FULL_NAME */
      if ( bdf->sf->copyright==NULL ) {
  	char *pt = strchr(bdf->sf->copyright,'\n'), *new;
  	if ( pt==NULL )
  	    BDFPropAddString(bdf,"COPYRIGHT",bdf->sf->copyright,onlyme);
  	else {
  	    new = copyn(bdf->sf->copyright,pt-bdf->sf->copyright);
  	    BDFPropAddString(bdf,"COPYRIGHT",new,onlyme);
  	    free(new);
  	}
      }
      if ( bdf->sf->version!=NULL )
  	BDFPropAddString(bdf,"FONT_VERSION",bdf->sf->version,onlyme );
      BDFPropAddInt(bdf,"FONT_ASCENT",bdf->ascent,onlyme);
      BDFPropAddInt(bdf,"FONT_DESCENT",bdf->descent,onlyme);
      BDFPropAddInt(bdf,"UNDERLINE_POSITION",
  	    (int) rint((bdf->sf->upos*bdf->pixelsize)/(bdf->sf->ascent+bdf->sf->descent)),onlyme);
      BDFPropAddInt(bdf,"UNDERLINE_THICKNESS",
  	    (int) ceil((bdf->sf->uwidth*bdf->pixelsize)/(bdf->sf->ascent+bdf->sf->descent)),onlyme);
      
      if ( x_h!=-1 )
  	BDFPropAddInt(bdf, "X_HEIGHT", x_h,onlyme );
      if ( cap_h!=-1 )
  	BDFPropAddInt(bdf, "CAP_HEIGHT", cap_h,onlyme );
      if ( def_ch!=-1 )
  	BDFPropAddInt(bdf, "DEFAULT_CHAR", def_ch,onlyme );
      BDFPropAddInt(bdf,"RAW_ASCENT",bdf->sf->ascent*1000/(bdf->sf->ascent+bdf->sf->descent),onlyme);
      BDFPropAddInt(bdf,"RAW_DESCENT",bdf->sf->descent*1000/(bdf->sf->ascent+bdf->sf->descent),onlyme);
      if ( bdf->sf->italicangle!=0 )
  	BDFPropAddInt(bdf,"ITALIC_ANGLE",(int) ((90+bdf->sf->italicangle)*64),onlyme);
  
      if ( (gid=SFFindExistingSlot(bdf->sf,' ',NULL))!=-1 && bdf->glyphs[gid]!=NULL )
  	BDFPropAddInt(bdf,"NORM_SPACE",bdf->glyphs[gid]->width,onlyme);
  
      if ( onlyme!=NULL ) {
  	/* Only generate these oddities if they ask for them... */
  	if ( strmatch(onlyme,"QUAD_WIDTH")==0 )		/* Depreciated */
  	    BDFPropAddInt(bdf,"QUAD_WIDTH",bdf->pixelsize,onlyme);
  	if ( components.res_x==components.res_y )	/* Depreciated */
  	    /* This isn't dpi (why not???!), it is 1/100 pixels per point */
  	    BDFPropAddInt(bdf,"RESOLUTION",7227/components.res_y,onlyme);
      }
  
      if ( bdf->sf->pfminfo.pfmset ) {
  	/* Only generate these if the user has set them with font info */
  	/* OS/2 uses values 0-900. XLFD uses 0-90 */
  	BDFPropAddInt(bdf,"RELATIVE_WEIGHT",bdf->sf->pfminfo.weight/10,onlyme);
  	BDFPropAddInt(bdf,"RELATIVE_SETWIDTH",bdf->sf->pfminfo.width*10,onlyme);
      }
      if ( bdf->sf->pfminfo.subsuper_set ) {
  	BDFPropAddInt(bdf,"SUPERSCRIPT_X",
  		bdf->sf->pfminfo.os2_supxoff*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
  	BDFPropAddInt(bdf,"SUPERSCRIPT_Y",
  		bdf->sf->pfminfo.os2_supyoff*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
  	BDFPropAddInt(bdf,"SUPERSCRIPT_SIZE",
  		bdf->sf->pfminfo.os2_supysize*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
  	BDFPropAddInt(bdf,"SUBSCRIPT_X",
  		bdf->sf->pfminfo.os2_subxoff*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
  	BDFPropAddInt(bdf,"SUBSCRIPT_Y",
  		bdf->sf->pfminfo.os2_subyoff*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
  	BDFPropAddInt(bdf,"SUBSCRIPT_SIZE",
  		bdf->sf->pfminfo.os2_subysize*bdf->pixelsize/(bdf->sf->ascent+bdf->sf->descent),
  		onlyme);
      }
  
      {
  	char *selection = "0123456789$";
  	int width=-1;
  	while ( *selection!='\0' ) {
  	    if ( (gid=SFFindExistingSlot(bdf->sf,*selection,NULL))!=-1 &&
  		    bdf->glyphs[gid]!=NULL ) {
  		if ( width==-1 )
  		    width = bdf->glyphs[gid]->width;
  		else if ( width!=bdf->glyphs[gid]->width )
  		    width = -2;
  	    }
  	    ++selection;
  	}
  	if ( width!=-2 )
  	    BDFPropAddInt(bdf,"FIGURE_WIDTH",width,onlyme);
      }
  
      {
  	int gid;
  	int lc_cnt, lc_sum, uc_cnt, uc_sum;
  	BDFChar *bdfc;
  
  	lc_cnt = lc_sum = uc_cnt = uc_sum = 0;
  	for ( gid = 0; gid<bdf->glyphcnt; ++gid ) if ( (bdfc=bdf->glyphs[gid])!=NULL ) {
  	    SplineChar *sc = bdfc->sc;
  	    if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && islower(sc->unicodeenc ) ) {
  		++lc_cnt;
  		lc_sum += bdfc->width;
  	    }
  	    if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && isupper(sc->unicodeenc ) ) {
  		++uc_cnt;
  		uc_sum += bdfc->width;
  	    }
  	}
  	if ( lc_cnt!=0 )
  	    BDFPropAddInt(bdf,"AVG_LOWERCASE_WIDTH",lc_sum*10/lc_cnt,onlyme);
  	if ( uc_cnt!=0 )
  	    BDFPropAddInt(bdf,"AVG_UPPERCASE_WIDTH",uc_sum*10/uc_cnt,onlyme);
      }
  
      /* Normally this does nothing, but fontforge may be configured to generate */
      /*  the obsolete _XFREE86_GLYPH_RANGES property, and if so this will */
      /*  generate them */
      GenerateGlyphRanges(bdf);
  
      /* MIN_SPACE, MAX_SPACE & END_SPACE are judgement calls and I shan't default them */
      /* also SMALL_CAP_SIZE, STRIKEOUT_ASCENT, STRIKEOUT_DESCENT, DESTINATION */
      /* WEIGHT requires a knowlege of stem sizes and I'm not going to do that */
      /*  much analysis */
      /* NOTICE is very similar to copyright. */
      /* FONT_TYPE, RASTERIZER_NAME, RASTERIZER_VERSION */
      /* RAW_* I only provide ASCENT & DESCENT */
      /* AXIS_NAMES, AXIS_LIMITS, AXIS_TYPES for mm fonts */
  }
  
  void BDFDefaultProps(BDFFont *bdf, EncMap *map, int res) {
      char *start, *end, *temp;
  
      bdf->prop_max = bdf->prop_cnt;
  
      Default_XLFD(bdf,map,res);
  
      if ( bdf->sf->copyright!=NULL ) {
  	start = bdf->sf->copyright;
  	while ( (end=strchr(start,'\n'))!=NULL ) {
  	    temp = copyn(start,end-start );
  	    BDFPropAppendString(bdf,"COMMENT", temp);
  	    free( temp );
  	    start = end+1;
  	}
  	if ( *start!='\0' )
  	    BDFPropAppendString(bdf,"COMMENT", start );
      }
      Default_Properties(bdf,map,NULL);
  }
  
  BDFChar *BDFMakeGID(BDFFont *bdf,int gid) {
      SplineFont *sf=bdf->sf;
      SplineChar *sc;
      BDFChar *bc;
      int i;
      extern int use_freetype_to_rasterize_fv;
  
      if ( gid==-1 )
  return( NULL );
  
      if ( sf->cidmaster!=NULL || sf->subfonts!=NULL ) {
  	int j = SFHasCID(sf,gid);
  	if ( sf->cidmaster ) sf = sf->cidmaster;
  	if ( j==-1 ) {
  	    for ( j=0; j<sf->subfontcnt; ++j )
  		if ( gid<sf->subfonts[j]->glyphcnt )
  	    break;
  	    if ( j==sf->subfontcnt )
  return( NULL );
  	}
  	sf = sf->subfonts[j];
      }
      if ( (sc = sf->glyphs[gid])==NULL )
  return( NULL );
  
      if ( gid>=bdf->glyphcnt ) {
  	if ( gid>=bdf->glyphmax )
  	    bdf->glyphs = grealloc(bdf->glyphs,(bdf->glyphmax=sf->glyphmax)*sizeof(BDFChar *));
  	for ( i=bdf->glyphcnt; i<=gid; ++i )
  	    bdf->glyphs[i] = NULL;
  	bdf->glyphcnt = sf->glyphcnt;
      }
      if ( (bc = bdf->glyphs[gid])==NULL ) {
  	if ( use_freetype_to_rasterize_fv ) {
  	    void *freetype_context = FreeTypeFontContext(sf,sc,NULL,ly_fore);
  	    if ( freetype_context != NULL ) {
  		bc = SplineCharFreeTypeRasterize(freetype_context,
  			sc->orig_pos,bdf->pixelsize,72,bdf->clut?8:1);
  		FreeTypeFreeContext(freetype_context);
  	    }
  	}
  	if ( bc!=NULL )
  	    /* Done */;
  	else if ( bdf->clut==NULL )
  	    bc = SplineCharRasterize(sc,ly_fore,bdf->pixelsize);
  	else
  	    bc = SplineCharAntiAlias(sc,ly_fore,bdf->pixelsize,BDFDepth(bdf));
  	bdf->glyphs[gid] = bc;
  	bc->orig_pos = gid;
  	BCCharChangedUpdate(bc);
      }
  return( bc );
  }
  
  BDFChar *BDFMakeChar(BDFFont *bdf,EncMap *map,int enc) {
      SplineFont *sf=bdf->sf;
  
      if ( enc==-1 )
  return( NULL );
  
      if ( sf->cidmaster!=NULL ) {
  	int j = SFHasCID(sf,enc);
  	sf = sf->cidmaster;
  	if ( j==-1 ) {
  	    for ( j=0; j<sf->subfontcnt; ++j )
  		if ( enc<sf->subfonts[j]->glyphcnt )
  	    break;
  	    if ( j==sf->subfontcnt )
  return( NULL );
  	}
  	sf = sf->subfonts[j];
      }
      SFMakeChar(sf,map,enc);
  return( BDFMakeGID(bdf,map->map[enc]));
  }
  
  void BCClearAll(BDFChar *bc) {
      BDFRefChar *head,*cur;
      
      if ( bc==NULL )
  return;
      for ( head = bc->refs; head != NULL; ) {
  	cur = head; head = head->next; free( cur );
      }
      bc->refs = NULL;
      
      BCPreserveState(bc);
      BCFlattenFloat(bc);
      memset(bc->bitmap,'\0',bc->bytes_per_line*(bc->ymax-bc->ymin+1));
      BCCompressBitmap(bc);
      bc->xmin = 0; bc->xmax = 0;
      bc->ymin = 0; bc->ymax = 0;
      BCCharChangedUpdate(bc);
  }
  
  static void BCChngNoUpdate(BDFChar *bc) {
      bc->changed = true;
      bc->sc->parent->changed = true;
  }
  
  static void BCNoUpdate(BDFChar *bc) {
  }
  
  static void BCNothingDestroyed(BDFChar *bc) {
  }
  
  static struct bc_interface noui_bc = {
      BCChngNoUpdate,
      BCNoUpdate,
      BCNothingDestroyed
  };
  
  struct bc_interface *bc_interface = &noui_bc;
  
  void FF_SetBCInterface(struct bc_interface *bci) {
      bc_interface = bci;
  }
  