unichar_t* Wordlist_advanceSelectedCharsBy( SplineFont* sf, EncMap *map, unichar_t* txtu, int offset ) { unichar_t original_data[ PATH_MAX ]; static unichar_t ret[ PATH_MAX ]; int i = 0; u_strcpy( original_data, txtu ); TRACE("Wordlist_advanceSelectedCharsBy(1) %s\n", u_to_c( txtu )); WordlistTrimTrailingSingleSlash( txtu ); WordListLine wll = WordlistEscapedInputStringToParsedData( sf, txtu ); int selectedCount = WordListLine_countSelected( wll ); if( !selectedCount ) wll->isSelected = 1; memset( ret, 0, sizeof(unichar_t) * PATH_MAX ); for( i = 0; wll->sc; wll++, i++ ) { SplineChar* sc = wll->sc; int element_selected = wll->isSelected; if( element_selected ) { int pos = map->backmap[ sc->orig_pos ]; pos += offset; int gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) { // we can't find a glyph at the desired position. // so instead of dropping it we just do not perform the operation // on this char. pos -= offset; gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) { // we can't go back manually! u_strcpy( ret, original_data ); return ret; } } if( gid==-1 || !sf->glyphs[gid] ) sc = SFMakeChar( sf, map, pos ); else sc = sf->glyphs[gid]; } if( element_selected ) uc_strcat( ret, "[" ); /* uc_strcat( ret, "/" ); */ /* uc_strcat( ret, scarray[i]->name ); */ uc_strcat( ret, Wordlist_getSCName( sc )); if( element_selected ) uc_strcat( ret, "]" ); } return ret; }
void FVDoit(CreateWidthData *wd) { FontViewBase *fv = (FontViewBase *) (wd->_fv); int i; BDFChar *bc; if ( fv->sf->onlybitmaps && fv->active_bitmap!=NULL && fv->sf->bitmaps!=NULL ) { double scale = (fv->sf->ascent+fv->sf->descent)/(double) fv->active_bitmap->pixelsize; wd->setto *= scale; wd->increment *= scale; } bc = NULL; for ( i=0; i<fv->map->enc_limit; ++i ) if ( fv->selected[i] ) { SplineChar *sc; sc = SFMakeChar(fv->sf,fv->map,i); if ( fv->sf->onlybitmaps && fv->sf->bitmaps!=NULL ) { if ( fv->active_bitmap!=NULL ) bc = BDFMakeChar(fv->active_bitmap,fv->map,i); #if 0 else { BDFFont *bdf; for ( bdf=fv->sf->bitmaps; bdf!=NULL; bdf=bdf->next ) bc = BDFMakeChar(bdf,fv->map,i); } #endif } DoChar(sc,wd,fv,bc); } wd->done = true; }
void ffw_import_svg_glyph(int code, const char * filename, double ox, double oy, double width) { int enc = SFFindSlot(cur_fv->sf, cur_fv->map, code, ""); if(enc == -1) return; SplineChar * sc = SFMakeChar(cur_fv->sf, cur_fv->map, enc); memset(cur_fv->selected, 0, cur_fv->map->enccount); cur_fv->selected[enc] = 1; int ok = FVImportImages(cur_fv, (char*)filename, fv_svg, 0, -1); if(!ok) err("Import SVG glyph failed"); // correct origin and width { int a = cur_fv->sf->ascent; int d = cur_fv->sf->descent; real transform[6]; transform[0] = 1.0; transform[3] = 1.0; transform[1] = transform[2] = 0.0; transform[4] = -ox * (a+d); transform[5] = -oy * (a+d) + d; FVTrans(cur_fv, sc, transform, NULL, fvt_alllayers | fvt_dontmovewidth); SCSynchronizeWidth(sc, floor(width * (a+d) + 0.5), sc->width, cur_fv); } }
/* * There is no check if a glyph with the same unicode exists! * TODO: let FontForge fill in the standard glyph name <- or maybe this might cause collision? */ void ffw_add_empty_char(int32_t unicode, int width) { SplineChar * sc = SFMakeChar(cur_fv->sf, cur_fv->map, cur_fv->map->enccount); char buffer[400]; SCSetMetaData(sc, strcopy(StdGlyphName(buffer, unicode, cur_fv->sf->uni_interp, cur_fv->sf->for_new_glyphs)), unicode, sc->comment); SCSynchronizeWidth(sc, width, sc->width, cur_fv); }
/* * TODO:bitmap, reference have not been considered in this function */ void ffw_set_widths(int * width_list, int mapping_len, int stretch_narrow, int squeeze_wide) { SplineFont * sf = cur_fv->sf; if(sf->onlybitmaps && cur_fv->active_bitmap != NULL && sf->bitmaps != NULL) { printf("TODO: width vs bitmap\n"); } EncMap * map = cur_fv->map; int i; int imax = min(mapping_len, map->enccount); for(i = 0; i < imax; ++i) { /* * Don't mess with it if the glyphs is not used. */ if(width_list[i] == -1) { continue; } int j = map->map[i]; if(j == -1) continue; SplineChar * sc = sf->glyphs[j]; if(sc == NULL) { sc = SFMakeChar(cur_fv->sf, cur_fv->map, j); } else if(((sc->width > EPS) && (((sc->width > width_list[i] + EPS) && (squeeze_wide)) || ((sc->width < width_list[i] - EPS) && (stretch_narrow))))) { real transform[6]; transform[0] = ((double)width_list[i]) / (sc->width); transform[3] = 1.0; transform[1] = transform[2] = transform[4] = transform[5] = 0; FVTrans(cur_fv, sc, transform, NULL, fvt_alllayers | fvt_dontmovewidth); } SCSynchronizeWidth(sc, width_list[i], sc->width, cur_fv); } }
unichar_t* Wordlist_selectionAdd( SplineFont* sf, EncMap *map, unichar_t* txtu, int offset ) { int i = 0; static unichar_t ret[ PATH_MAX ]; memset( ret, 0, sizeof(unichar_t) * PATH_MAX ); WordlistTrimTrailingSingleSlash( txtu ); WordListLine wll = WordlistEscapedInputStringToParsedData( sf, txtu ); for( i = 0; wll->sc; wll++, i++ ) { SplineChar* sc = wll->sc; int element_selected = wll->isSelected; if( i == offset ) element_selected = 1; if( element_selected ) { int pos = map->backmap[ sc->orig_pos ]; TRACE("pos1:%d\n", pos ); TRACE("map:%d\n", map->map[ pos ] ); int gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) continue; if( gid==-1 || !sf->glyphs[gid] ) sc = SFMakeChar( sf, map, pos ); else sc = sf->glyphs[gid]; } if( element_selected ) uc_strcat( ret, "[" ); /* uc_strcat( ret, "/" ); */ /* uc_strcat( ret, scarray[i]->name ); */ uc_strcat( ret, Wordlist_getSCName( sc )); if( element_selected ) uc_strcat( ret, "]" ); } return ret; }
void ffw_make_char(int enc, int width) { SFMakeChar(cur_fv->sf, cur_fv->map, enc)->width = width; }
SplineFont *SFReadIkarus(char *fontname) { SplineFont *sf; FILE *file = fopen(fontname,"rb"); int ch1, ch2, rpos, wpos, i; int hlen, ilen, jlen, llen, mlen; int numchars, maxnum, opt_pt_size; double italic_angle; char fnam[13], fullname[81]; int32 *offsets, *numbers; if ( file==NULL ) return( NULL ); hlen = getushort(file); /* Length of font header */ ilen = getushort(file); /* Length of name section */ getushort(file); /* Number on URW list */ fread(fnam,1,12,file); /* 6 words of filename */ fread(fullname,1,80,file); /* 40 words of fontname (human readable) */ fnam[12] = fullname[80] = '\0'; ch1 = getc(file); ch2 = getc(file); if ( ch1=='i' || ch2=='k' ) /* Docs don't mention this, but lower case is ok too */; else if ( ch1!='I' || ch2!='K' ) { if ( (ch1=='D' && ch2=='I') || (ch1=='V' && ch2=='C') || (ch1=='V' && ch2=='S') || (ch1=='V' && ch2=='E') || (ch1=='S' && ch2=='C') || (ch1=='S' && ch2=='N') || (ch1=='B' && ch2=='I') || (ch1=='G' && isdigit(ch2)) || (ch1=='d' && ch2=='i') || (ch1=='v' && ch2=='c') || (ch1=='v' && ch2=='s') || (ch1=='v' && ch2=='e') || (ch1=='s' && ch2=='c') || (ch1=='s' && ch2=='n') || (ch1=='b' && ch2=='i') || (ch1=='g' && isdigit(ch2))) LogError( _("This is probably a valid URW font, but it is in a format (%c%c) which FontForge\ndoes not support. FontForge only supports 'IK' format fonts.\n"), ch1, ch2 ); else if ( ch1==0 && ch2==0 && ilen==55 ) LogError( _("This looks like an ikarus format which I have seen examples of, but for which\nI have no documentation. FontForge does not support it yet.\n") ); fclose(file); return( NULL ); } else if ( ilen<55 || hlen<=ilen ) { fclose(file); return( NULL ); } if ( ilen!=55 ) LogError( _("Unexpected size for name section of URW font (expected 55, got %d)\n"), ilen ); fseek(file,2*ilen+2,SEEK_SET); jlen = getushort(file); if ( jlen<12 || hlen<=jlen+ilen ) { fclose(file); return( NULL ); } if ( jlen!=12 ) LogError( _("Unexpected size for font info section of URW font (expected 12, got %d)\n"), jlen ); if ( getushort(file)!=1 ) { /* 1=> typeface */ fclose(file); return( NULL ); } numchars = getushort(file); /* cap height = */ getushort(file); /* body size = */ getushort(file); /* x-height = */ getushort(file); /* descender? = */ getushort(file); /* line thickness = */ getushort(file); /* stroke thickness = */ getushort(file); italic_angle = getushort(file)/10.0 * 3.1415926535897932/180.0; opt_pt_size = getushort(file); /* average char width = */ getushort(file); fseek(file,2*ilen+2*jlen+2,SEEK_SET); llen = getushort(file); /* the hierarchy section is unused in font files */ if ( llen!=1 || hlen<=jlen+ilen+llen ) { fclose(file); return( NULL ); } mlen = getushort(file); /* Peter Karow's book documents that hlen==jlen+ilen+llen+mlen+1, but this*/ /* does not appear to be the case. */ if ( hlen<jlen+ilen+llen+mlen+1 || mlen<3*numchars+3 ) { fclose(file); return( NULL ); } /* last record */ getushort(file); /* last word of last record */ getushort(file); offsets = malloc(numchars*sizeof(int32)); numbers = malloc(numchars*sizeof(int32)); maxnum = 0; for ( i=0; i<numchars; ++i ) { numbers[i] = getushort(file); if ( numbers[i]>maxnum ) maxnum = numbers[i]; rpos = getushort(file); /* record pos (1 record=2048 words) */ wpos = getushort(file); /* word pos in record */ offsets[i] = (rpos-1)*4096 + 2*(wpos-1); } sf = SplineFontBlank(numchars/*maxnum+1*/); IkarusFontname(sf,fullname,fnam); sf->italicangle = italic_angle; sf->ascent = 12000; sf->descent = 3000; /* Ikarus fonts live in a 15,000 em world */ for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) sf->glyphs[i]->width = sf->glyphs[i]->vwidth = 15000; sf->map = EncMapNew(numchars,numchars,&custom); for ( i=0; i<numchars; ++i ) { fseek(file,offsets[i],SEEK_SET); IkarusReadChar(SFMakeChar(sf,sf->map,i),file); } if ( loaded_fonts_same_as_new && new_fonts_are_order2 ) SFConvertToOrder2(sf); free(numbers); free(offsets); fclose(file); return( sf ); }
unichar_t* Wordlist_advanceSelectedCharsBy( SplineFont* sf, EncMap *map, unichar_t* txtu, int offset ) { unichar_t original_data[ PATH_MAX ]; static unichar_t ret[ PATH_MAX ]; int limit = PATH_MAX; SplineChar* scarray[ PATH_MAX + 1 ]; GArray* selected = 0; int i = 0; u_strcpy( original_data, txtu ); TRACE("Wordlist_advanceSelectedCharsBy(1) %s\n", u_to_c( txtu )); WordlistTrimTrailingSingleSlash( txtu ); txtu = WordlistEscapedInputStringToRealStringBasic( sf, txtu, &selected ); TRACE("Wordlist_advanceSelectedCharsBy(2) %s\n", u_to_c( txtu )); GArray* bv = Wordlist_selectedToBitmapArray( selected ); TRACE("selected->len:%d\n", selected->len ); if( !selected->len ) { int one = 1; g_array_insert_val( bv, 0, one ); } g_array_unref( selected ); selected = 0; memset( scarray, 0, sizeof(SplineChar*) * limit+1 ); const unichar_t *pt, *ept, *tpt; pt = txtu; ept=txtu+u_strlen(txtu); for ( tpt=pt; tpt<ept; ++tpt ) { int ch = *tpt; if( tpt == pt ) { // your own char at the leading of the text SplineChar* sc = SFGetOrMakeCharFromUnicodeBasic( sf, ch ); scarray[i] = sc; i++; continue; } scarray[i] = SFGetOrMakeCharFromUnicodeBasic( sf, ch ); i++; if( i >= limit ) break; } memset( ret, 0, sizeof(unichar_t) * PATH_MAX ); for( i = 0; scarray[i]; i++ ) { int element_selected = g_array_index (bv, gint, i); if( element_selected ) { int pos = map->backmap[ scarray[i]->orig_pos ]; TRACE("pos1:%d\n", pos ); pos += offset; TRACE("pos2:%d\n", pos ); TRACE("map:%d\n", map->map[ pos ] ); int gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) { // we can't find a glyph at the desired position. // so instead of dropping it we just do not perform the operation // on this char. pos -= offset; gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) { // we can't go back manually! printf("no glyph!\n"); u_strcpy( ret, original_data ); return ret; } } if( gid==-1 || !sf->glyphs[gid] ) scarray[i] = SFMakeChar( sf, map, pos ); else scarray[i] = sf->glyphs[gid]; } if( element_selected ) uc_strcat( ret, "[" ); /* uc_strcat( ret, "/" ); */ /* uc_strcat( ret, scarray[i]->name ); */ uc_strcat( ret, Wordlist_getSCName( scarray[i] )); if( element_selected ) uc_strcat( ret, "]" ); } TRACE("Wordlist_advanceSelectedCharsBy(e) %s\n", u_to_c( ret )); return ret; }
unichar_t* Wordlist_selectionAdd( SplineFont* sf, EncMap *map, unichar_t* txtu, int offset ) { int i = 0; static unichar_t ret[ PATH_MAX ]; int limit = PATH_MAX; SplineChar* scarray[ PATH_MAX + 1 ]; GArray* selected = 0; memset( ret, 0, sizeof(unichar_t) * PATH_MAX ); memset( scarray, 0, sizeof(SplineChar*) * limit+1 ); WordlistTrimTrailingSingleSlash( txtu ); txtu = WordlistEscapedInputStringToRealStringBasic( sf, txtu, &selected ); GArray* bv = Wordlist_selectedToBitmapArray( selected ); g_array_unref( selected ); selected = 0; const unichar_t *pt, *ept, *tpt; pt = txtu; ept=txtu+u_strlen(txtu); for ( tpt=pt; tpt<ept; ++tpt ) { int ch = *tpt; if( tpt == pt ) { // your own char at the leading of the text SplineChar* sc = SFGetOrMakeCharFromUnicodeBasic( sf, ch ); scarray[i] = sc; i++; continue; } scarray[i] = SFGetOrMakeCharFromUnicodeBasic( sf, ch ); i++; if( i >= limit ) break; } memset( ret, 0, sizeof(unichar_t) * PATH_MAX ); for( i = 0; scarray[i]; i++ ) { int element_selected = g_array_index (bv, gint, i); if( i == offset ) element_selected = 1; if( element_selected ) { int pos = map->backmap[ scarray[i]->orig_pos ]; TRACE("pos1:%d\n", pos ); TRACE("map:%d\n", map->map[ pos ] ); int gid = pos < 0 || pos >= map->enccount ? -2 : map->map[pos]; if( gid == -2 ) continue; if( gid==-1 || !sf->glyphs[gid] ) scarray[i] = SFMakeChar( sf, map, pos ); else scarray[i] = sf->glyphs[gid]; } if( element_selected ) uc_strcat( ret, "[" ); /* uc_strcat( ret, "/" ); */ /* uc_strcat( ret, scarray[i]->name ); */ uc_strcat( ret, Wordlist_getSCName( scarray[i] )); if( element_selected ) uc_strcat( ret, "]" ); } return ret; }
static int FNT_Load(FILE *fnt,SplineFont *sf) { struct fntheader fntheader; struct v3chars charinfo[258]; /* Max size */ int i, j, k, ch; uint32 base = ftell(fnt); char *pt, *spt, *temp; BDFFont *bdf; BDFChar *bdfc; memset(&fntheader,0,sizeof(fntheader)); fntheader.version = lgetushort(fnt); if ( fntheader.version != 0x200 && fntheader.version != 0x300 ) return( false ); fntheader.filesize = lgetlong(fnt); for ( i=0; i<60; ++i ) fntheader.copyright[i] = getc(fnt); fntheader.copyright[i] = '\0'; for ( --i; i>=0 && fntheader.copyright[i]==' '; --i ) fntheader.copyright[i] = '\0'; fntheader.type = lgetushort(fnt); if ( fntheader.type & (FNT_TYPE_VECTOR|FNT_TYPE_MEMORY|FNT_TYPE_DEVICE)) return( false ); fntheader.pointsize = lgetushort(fnt); fntheader.vertres = lgetushort(fnt); fntheader.hortres = lgetushort(fnt); fntheader.ascent = lgetushort(fnt); fntheader.internal_leading = lgetushort(fnt); fntheader.external_leading = lgetushort(fnt); fntheader.italic = getc(fnt); fntheader.underline = getc(fnt); fntheader.strikeout = getc(fnt); fntheader.weight = lgetushort(fnt); fntheader.charset = getc(fnt); fntheader.width = lgetushort(fnt); fntheader.height = lgetushort(fnt); fntheader.pitchfamily = getc(fnt); fntheader.avgwidth = lgetushort(fnt); fntheader.maxwidth = lgetushort(fnt); fntheader.firstchar = getc(fnt); fntheader.lastchar = getc(fnt); fntheader.defchar = getc(fnt); fntheader.breakchar = getc(fnt); fntheader.widthbytes = lgetushort(fnt); fntheader.deviceoffset = lgetlong(fnt); fntheader.faceoffset = lgetlong(fnt); fntheader.bitspointer = lgetlong(fnt); fntheader.bitsoffset = lgetlong(fnt); (void) getc(fnt); /* Not documented in the v2 spec but seems to be present */ if ( fntheader.version == 0x300 ) { fntheader.flags = lgetlong(fnt); if ( fntheader.flags & (FNT_FLAGS_ABCFIXED|FNT_FLAGS_ABCPROP|FNT_FLAGS_16COLOR|FNT_FLAGS_256COLOR|FNT_FLAGS_RGBCOLOR)) return( false ); fntheader.aspace = lgetushort(fnt); fntheader.bspace = lgetushort(fnt); fntheader.cspace = lgetushort(fnt); fntheader.coloroffset = lgetlong(fnt); for ( i=0; i<16; ++i ) /* Freetype thinks this is 4 */ (void) getc(fnt); } memset(charinfo,0,sizeof(charinfo)); for ( i=fntheader.firstchar; i<=fntheader.lastchar+2; ++i ) { charinfo[i].width = lgetushort(fnt); if ( fntheader.version==0x200 ) charinfo[i].offset = lgetushort(fnt); else charinfo[i].offset = lgetlong(fnt); } /* Set the font names and the pfminfo structure */ sf->pfminfo.pfmset = true; if ( fntheader.copyright[0]!='\0' ) sf->copyright = copy(fntheader.copyright); sf->weight = copy( fntheader.weight<=100 ? "Thin" : fntheader.weight<=200 ? "Extralight" : fntheader.weight<=300 ? "Light" : fntheader.weight<=400 ? "Normal" : fntheader.weight<=500 ? "Medium" : fntheader.weight<=600 ? "Demibold" : fntheader.weight<=700 ? "Bold" : fntheader.weight<=800 ? "Heavy" : fntheader.weight<=900 ? "Black" : "Nord" ); sf->pfminfo.weight = fntheader.weight; sf->pfminfo.panose[2] = fntheader.weight/100 + 1; fseek(fnt,base+fntheader.faceoffset,SEEK_SET); for ( i=0; (ch=getc(fnt))!=EOF && ch!=0; ++i ); sf->familyname = malloc(i+2); fseek(fnt,base+fntheader.faceoffset,SEEK_SET); for ( i=0; (ch=getc(fnt))!=EOF && ch!=0; ++i ) sf->familyname[i] = ch; sf->familyname[i] = '\0'; temp = malloc(i+50); strcpy(temp,sf->familyname); if ( fntheader.weight<=300 && fntheader.weight>500 ) { strcat(temp," "); strcat(temp,sf->weight); } if ( fntheader.italic ) strcat(temp," Italic"); sf->fullname = temp; sf->fontname = copy(sf->fullname); for ( pt=spt=sf->fontname; *spt; ++spt ) if ( *spt!=' ' ) *pt++ = *spt; *pt = '\0'; sf->pfminfo.pfmfamily = fntheader.pitchfamily; sf->pfminfo.panose[0] = 2; if ( (fntheader.pitchfamily&FNT_FAMILY_MASK)==FNT_FAMILY_SCRIPT ) sf->pfminfo.panose[0] = 3; sf->pfminfo.width = 5; /* No info about condensed/extended */ sf->pfminfo.panose[3] = 3; if ( !(fntheader.pitchfamily&FNT_PITCH_VARIABLE ) ) sf->pfminfo.panose[3] = 9; sf->pfminfo.linegap = (sf->ascent+sf->descent)*fntheader.external_leading/fntheader.height; if ( fntheader.italic ) sf->italicangle = 11.25; bdf = calloc(1,sizeof(BDFFont)); bdf->sf = sf; bdf->glyphcnt = sf->glyphcnt; bdf->glyphmax = sf->glyphmax; bdf->res = fntheader.vertres; bdf->pixelsize = rint(fntheader.pointsize*fntheader.vertres/72.27); bdf->glyphs = calloc(sf->glyphmax,sizeof(BDFChar *)); bdf->ascent = rint(.8*bdf->pixelsize); /* shouldn't use typographical ascent */ bdf->descent = bdf->pixelsize-bdf->ascent; for ( i=fntheader.firstchar; i<=fntheader.lastchar; ++i ) if ( charinfo[i].width!=0 ) { int gid = SFMakeChar(sf,sf->map,i)->orig_pos; if ( gid>=bdf->glyphcnt ) { if ( gid>=bdf->glyphmax ) bdf->glyphs = realloc(bdf->glyphs,(bdf->glyphmax=sf->glyphmax)*sizeof(BDFChar *)); memset(bdf->glyphs+bdf->glyphcnt,0,(sf->glyphcnt-bdf->glyphcnt)*sizeof(BDFChar *)); bdf->glyphcnt = sf->glyphcnt; } bdf->glyphs[gid] = bdfc = XZALLOC(BDFChar); memset( bdfc,'\0',sizeof( BDFChar )); bdfc->xmin = 0; bdfc->xmax = charinfo[i].width-1; bdfc->ymin = fntheader.ascent-fntheader.height; bdfc->ymax = fntheader.ascent-1; bdfc->width = charinfo[i].width; bdfc->vwidth = bdf->pixelsize; bdfc->bytes_per_line = (bdfc->xmax>>3)+1; bdfc->bitmap = calloc(bdfc->bytes_per_line*fntheader.height,sizeof(uint8)); bdfc->orig_pos = gid; bdfc->sc = sf->glyphs[gid]; bdfc->sc->widthset = true; fseek(fnt,base+charinfo[i].offset,SEEK_SET); for ( j=0; j<bdfc->bytes_per_line; ++j ) { for ( k=0; k<fntheader.height; ++k ) bdfc->bitmap[k*bdfc->bytes_per_line+j] = getc(fnt); } BCCompressBitmap(bdfc); if ( feof(fnt) ) return( false ); }
/* * There is no check if a glyph with the same unicode exists! */ void ffw_add_empty_char(int32_t unicode, int width) { SplineChar * sc = SFMakeChar(cur_fv->sf, cur_fv->map, cur_fv->map->enccount); SCSetMetaData(sc, sc->name, unicode, sc->comment); SCSynchronizeWidth(sc, width, sc->width, cur_fv); }