1 /* Copyright (C) 2000-2008 by George Williams */
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "fontforgevw.h"
33 #include <sys/types.h>
37 #include "unicoderange.h"
41 #define MKDIR(A,B) mkdir(A)
43 #define MKDIR(A,B) mkdir(A,B)
47 void SFUntickAll(SplineFont
*sf
) {
50 for ( i
=0; i
<sf
->glyphcnt
; ++i
) if ( sf
->glyphs
[i
]!=NULL
)
51 sf
->glyphs
[i
]->ticked
= false;
54 SplineChar
*SCBuildDummy(SplineChar
*dummy
,SplineFont
*sf
,EncMap
*map
,int i
) {
55 static char namebuf
[100];
56 static Layer layers
[2];
58 memset(dummy
,'\0',sizeof(*dummy
));
59 dummy
->color
= COLOR_DEFAULT
;
61 dummy
->layers
= layers
;
62 if ( sf
->cidmaster
!=NULL
) {
63 /* CID fonts don't have encodings, instead we must look up the cid */
64 if ( sf
->cidmaster
->loading_cid_map
)
65 dummy
->unicodeenc
= -1;
67 dummy
->unicodeenc
= CID2NameUni(FindCidMap(sf
->cidmaster
->cidregistry
,sf
->cidmaster
->ordering
,sf
->cidmaster
->supplement
,sf
->cidmaster
),
68 i
,namebuf
,sizeof(namebuf
));
70 dummy
->unicodeenc
= UniFromEnc(i
,map
->enc
);
72 if ( sf
->cidmaster
!=NULL
)
73 dummy
->name
= namebuf
;
74 else if ( map
->enc
->psnames
!=NULL
&& i
<map
->enc
->char_cnt
&&
75 map
->enc
->psnames
[i
]!=NULL
)
76 dummy
->name
= map
->enc
->psnames
[i
];
77 else if ( dummy
->unicodeenc
==-1 )
80 dummy
->name
= (char *) StdGlyphName(namebuf
,dummy
->unicodeenc
,sf
->uni_interp
,sf
->for_new_glyphs
);
81 if ( dummy
->name
==NULL
) {
82 /*if ( dummy->unicodeenc!=-1 || i<256 )
83 dummy->name = ".notdef";
86 sprintf( namebuf
, "NameMe.%d", i
);
88 while ( SFFindExistingSlot(sf
,-1,namebuf
)!=-1 )
89 sprintf( namebuf
, "NameMe.%d.%d", i
, ++j
);
90 dummy
->name
= namebuf
;
93 dummy
->width
= dummy
->vwidth
= sf
->ascent
+sf
->descent
;
94 if ( dummy
->unicodeenc
>0 && dummy
->unicodeenc
<0x10000 &&
95 iscombining(dummy
->unicodeenc
)) {
96 /* Mark characters should be 0 width */
98 /* Except in monospaced fonts on windows, where they should be the */
99 /* same width as everything else */
101 /* Actually, in a monospace font, all glyphs should be the same width */
102 /* whether mark or not */
103 if ( sf
->pfminfo
.panose_set
&& sf
->pfminfo
.panose
[3]==9 &&
105 for ( i
=sf
->glyphcnt
-1; i
>=0; --i
)
106 if ( SCWorthOutputting(sf
->glyphs
[i
])) {
107 dummy
->width
= sf
->glyphs
[i
]->width
;
112 dummy
->orig_pos
= 0xffff;
116 static SplineChar
*_SFMakeChar(SplineFont
*sf
,EncMap
*map
,int enc
) {
117 SplineChar dummy
, *sc
;
119 int j
, real_uni
, gid
;
120 extern const int cns14pua
[], amspua
[];
122 if ( enc
>=map
->enccount
)
126 if ( sf
->subfontcnt
!=0 && gid
!=-1 ) {
128 for ( j
=0; j
<sf
->subfontcnt
; ++j
)
129 if ( gid
<sf
->subfonts
[j
]->glyphcnt
) {
130 ssf
= sf
->subfonts
[j
];
131 if ( ssf
->glyphs
[gid
]!=NULL
) {
132 return( ssf
->glyphs
[gid
] );
138 if ( gid
==-1 || (sc
= sf
->glyphs
[gid
])==NULL
) {
139 if (( map
->enc
->is_unicodebmp
|| map
->enc
->is_unicodefull
) &&
140 ( enc
>=0xe000 && enc
<=0xf8ff ) &&
141 ( sf
->uni_interp
==ui_ams
|| sf
->uni_interp
==ui_trad_chinese
) &&
142 ( real_uni
= (sf
->uni_interp
==ui_ams
? amspua
: cns14pua
)[enc
-0xe000])!=0 ) {
143 if ( real_uni
<map
->enccount
) {
145 /* if necessary, create the real unicode code point */
146 /* and then make us be a duplicate of it */
147 sc
= _SFMakeChar(sf
,map
,real_uni
);
148 map
->map
[enc
] = gid
= sc
->orig_pos
;
149 SCCharChangedUpdate(sc
,ly_all
);
154 SCBuildDummy(&dummy
,sf
,map
,enc
);
155 /* Let's say a user has a postscript encoding where the glyph ".notdef" */
156 /* is assigned to many slots. Once the user creates a .notdef glyph */
157 /* all those slots should fill in. If they don't they damn well better*/
158 /* when the user clicks on one to edit it */
159 /* Used to do that with all encodings. It just confused people */
160 if ( map
->enc
->psnames
!=NULL
&&
161 (sc
= SFGetChar(sf
,dummy
.unicodeenc
,dummy
.name
))!=NULL
) {
162 map
->map
[enc
] = sc
->orig_pos
;
165 sc
= SFSplineCharCreate(sf
);
166 sc
->unicodeenc
= dummy
.unicodeenc
;
167 sc
->name
= copy(dummy
.name
);
168 sc
->width
= dummy
.width
;
169 sc
->orig_pos
= 0xffff;
170 /*SCLigDefault(sc);*/
171 SFAddGlyphAndEncode(sf
,sc
,map
,enc
);
176 SplineChar
*SFMakeChar(SplineFont
*sf
,EncMap
*map
, int enc
) {
181 if ( enc
>=map
->enccount
)
185 if ( sf
->mm
!=NULL
&& (gid
==-1 || sf
->glyphs
[gid
]==NULL
) ) {
187 _SFMakeChar(sf
->mm
->normal
,map
,enc
);
188 for ( j
=0; j
<sf
->mm
->instance_count
; ++j
)
189 _SFMakeChar(sf
->mm
->instances
[j
],map
,enc
);
191 return( _SFMakeChar(sf
,map
,enc
));
194 struct unicoderange specialnames
[] = {
195 { NULL
, 0, 0, 0, 0, 0, 0 }
199 static SplineFont
*_SFReadPostscript(FILE *file
,char *filename
) {
203 ff_progress_change_stages(2);
204 fd
= _ReadPSFont(file
);
205 ff_progress_next_stage();
206 ff_progress_change_line2(_("Interpreting Glyphs"));
208 sf
= SplineFontFromPSFont(fd
);
211 CheckAfmOfPostscript(sf
,filename
,sf
->map
);
216 static SplineFont
*SFReadPostscript(char *filename
) {
220 ff_progress_change_stages(2);
221 fd
= ReadPSFont(filename
);
222 ff_progress_next_stage();
223 ff_progress_change_line2(_("Interpreting Glyphs"));
225 sf
= SplineFontFromPSFont(fd
);
228 CheckAfmOfPostscript(sf
,filename
,sf
->map
);
234 struct compressors compressors
[] = {
235 { ".gz", "gunzip", "gzip" },
236 { ".bz2", "bunzip2", "bzip2" },
237 { ".bz", "bunzip2", "bzip2" },
238 { ".Z", "gunzip", "compress" },
239 /* file types which are both archived and compressed (.tgz, .zip) are handled */
240 /* by the archiver above */
244 char *Decompress(char *name
, int compression
) {
245 char *dir
= getenv("TMPDIR");
249 if ( dir
==NULL
) dir
= P_tmpdir
;
250 tmpfile
= galloc(strlen(dir
)+strlen(GFileNameTail(name
))+2);
253 strcat(tmpfile
,GFileNameTail(name
));
254 *strrchr(tmpfile
,'.') = '\0';
255 #if defined( _NO_SNPRINTF ) || defined( __VMS )
256 sprintf( buf
, "%s < %s > %s", compressors
[compression
].decomp
, name
, tmpfile
);
258 snprintf( buf
, sizeof(buf
), "%s < %s > %s", compressors
[compression
].decomp
, name
, tmpfile
);
260 if ( system(buf
)==0 )
266 static char *ForceFileToHaveName(FILE *file
, char *exten
) {
267 char tmpfilename
[L_tmpnam
+100];
272 sprintf( tmpfilename
, P_tmpdir
"/fontforge%d-%d", getpid(), try++ );
274 strcat(tmpfilename
,exten
);
275 if ( access( tmpfilename
, F_OK
)==-1 &&
276 (newfile
= fopen(tmpfilename
,"w"))!=NULL
) {
279 while ( (len
= fread(buffer
,1,sizeof(buffer
),file
))>0 )
280 fwrite(buffer
,1,len
,newfile
);
283 return(copy(tmpfilename
)); /* The filename does not exist */
287 /* This does not check currently existing fontviews, and should only be used */
288 /* by LoadSplineFont (which does) and by RevertFile (which knows what it's doing) */
289 SplineFont
*_ReadSplineFont(FILE *file
,char *filename
,enum openflags openflags
) {
291 char ubuf
[250], *temp
;
294 char *pt
, *strippedname
, *oldstrippedname
, *tmpfile
=NULL
, *paren
=NULL
, *fullname
=filename
, *rparen
;
298 int wasurl
= false, nowlocal
= true;
300 if ( filename
==NULL
)
303 strippedname
= filename
;
304 pt
= strrchr(filename
,'/');
305 if ( pt
==NULL
) pt
= filename
;
306 /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
307 /* that ff wouldn't open it */
308 /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
309 if ( (paren
= strrchr(pt
,'('))!=NULL
&&
310 (rparen
= strrchr(paren
,')'))!=NULL
&&
312 strippedname
= copy(filename
);
313 strippedname
[paren
-filename
] = '\0';
316 pt
= strrchr(strippedname
,'.');
319 if ( pt
!=NULL
) for ( i
=0; compressors
[i
].ext
!=NULL
; ++i
)
320 if ( strcmp(compressors
[i
].ext
,pt
)==0 )
322 oldstrippedname
= strippedname
;
323 if ( i
==-1 || compressors
[i
].ext
==NULL
)
327 char *spuriousname
= ForceFileToHaveName(file
,compressors
[i
].ext
);
328 tmpfile
= Decompress(spuriousname
,i
);
329 fclose(file
); file
= NULL
;
330 unlink(spuriousname
); free(spuriousname
);
332 tmpfile
= Decompress(strippedname
,i
);
333 if ( tmpfile
!=NULL
) {
334 strippedname
= tmpfile
;
336 ff_post_error(_("Decompress Failed!"),_("Decompress Failed!"));
340 if ( strippedname
!=filename
&& paren
!=NULL
) {
341 fullname
= galloc(strlen(strippedname
)+strlen(paren
)+1);
342 strcpy(fullname
,strippedname
);
343 strcat(fullname
,paren
);
345 fullname
= strippedname
;
348 /* If there are no pfaedit windows, give them something to look at */
349 /* immediately. Otherwise delay a bit */
350 strcpy(ubuf
,_("Loading font from "));
352 if ( !wasurl
|| i
==-1 ) /* If it wasn't compressed, or it wasn't an url, then the fullname is reasonable, else use the original name */
353 strncat(ubuf
,temp
= copy(GFileNameTail(fullname
)),100);
355 strncat(ubuf
,temp
= copy(GFileNameTail(filename
)),100);
357 ubuf
[100+len
] = '\0';
358 ff_progress_start_indicator(FontViewFirst()==NULL
?0:10,_("Loading..."),ubuf
,_("Reading Glyphs"),0,1);
359 ff_progress_enable_stop(0);
362 file
= fopen(strippedname
,"rb");
368 /* checked == false => not checked */
369 /* checked == 'u' => UFO */
370 /* checked == 't' => TTF/OTF */
371 /* checked == 'p' => pfb/general postscript */
372 /* checked == 'P' => pdf */
373 /* checked == 'c' => cff */
374 /* checked == 'S' => svg */
375 /* checked == 'f' => sfd */
376 /* checked == 'F' => sfdir */
377 /* checked == 'b' => bdf */
378 /* checked == 'i' => ikarus */
380 /* Try to guess the file type from the first few characters... */
381 int ch1
= getc(file
);
382 int ch2
= getc(file
);
383 int ch3
= getc(file
);
384 int ch4
= getc(file
);
386 fseek(file
, 98, SEEK_SET
);
390 if (( ch1
==0 && ch2
==1 && ch3
==0 && ch4
==0 ) ||
391 (ch1
=='O' && ch2
=='T' && ch3
=='T' && ch4
=='O') ||
392 (ch1
=='t' && ch2
=='r' && ch3
=='u' && ch4
=='e') ||
393 (ch1
=='t' && ch2
=='t' && ch3
=='c' && ch4
=='f') ) {
394 sf
= _SFReadTTF(file
,0,openflags
,fullname
,NULL
);
396 } else if (( ch1
=='%' && ch2
=='!' ) ||
397 ( ch1
==0x80 && ch2
=='\01' ) ) { /* PFB header */
398 sf
= _SFReadPostscript(file
,fullname
);
400 } else if ( ch1
==1 && ch2
==0 && ch3
==4 ) {
402 fseek(file
,0,SEEK_END
);
404 fseek(file
,0,SEEK_SET
);
405 sf
= _CFFParse(file
,len
,NULL
);
407 } /* Too hard to figure out a valid mark for a mac resource file */
408 if ( file
!=NULL
) fclose(file
);
413 else if (( strmatch(fullname
+strlen(fullname
)-4, ".ttf")==0 ||
414 strmatch(fullname
+strlen(strippedname
)-4, ".ttc")==0 ||
415 strmatch(fullname
+strlen(fullname
)-4, ".gai")==0 ||
416 strmatch(fullname
+strlen(fullname
)-4, ".otf")==0 ||
417 strmatch(fullname
+strlen(fullname
)-4, ".otb")==0 ) && checked
!='t') {
418 sf
= SFReadTTF(fullname
,0,openflags
);
419 } else if ( strmatch(fullname
+strlen(strippedname
)-4, ".bin")==0 ||
420 strmatch(fullname
+strlen(strippedname
)-4, ".hqx")==0 ||
421 strmatch(fullname
+strlen(strippedname
)-6, ".dfont")==0 ) {
422 sf
= SFReadMacBinary(fullname
,0,openflags
);
423 } else if ( (strmatch(fullname
+strlen(fullname
)-4, ".pfa")==0 ||
424 strmatch(fullname
+strlen(fullname
)-4, ".pfb")==0 ||
425 strmatch(fullname
+strlen(fullname
)-4, ".pf3")==0 ||
426 strmatch(fullname
+strlen(fullname
)-4, ".cid")==0 ||
427 strmatch(fullname
+strlen(fullname
)-4, ".gsf")==0 ||
428 strmatch(fullname
+strlen(fullname
)-4, ".pt3")==0 ||
429 strmatch(fullname
+strlen(fullname
)-3, ".ps")==0 ) && checked
!='p' ) {
430 sf
= SFReadPostscript(fullname
);
431 } else if ( strmatch(fullname
+strlen(fullname
)-4, ".cff")==0 && checked
!='c' ) {
432 sf
= CFFParse(fullname
);
434 sf
= SFReadMacBinary(fullname
,0,openflags
);
436 ff_progress_end_indicator();
439 SplineFont
*norm
= sf
->mm
!=NULL
? sf
->mm
->normal
: sf
;
440 if ( compression
!=0 ) {
442 *strrchr(oldstrippedname
,'.') = '\0';
443 sf
->filename
= copy( oldstrippedname
);
446 sf
->compression
= compression
;
447 free( norm
->origname
);
448 if ( sf
->chosenname
!=NULL
&& strippedname
==filename
) {
449 norm
->origname
= galloc(strlen(filename
)+strlen(sf
->chosenname
)+8);
450 strcpy(norm
->origname
,filename
);
451 strcat(norm
->origname
,"(");
452 strcat(norm
->origname
,sf
->chosenname
);
453 strcat(norm
->origname
,")");
455 norm
->origname
= copy(filename
);
456 free( norm
->chosenname
); norm
->chosenname
= NULL
;
457 if ( sf
->mm
!=NULL
) {
459 for ( j
=0; j
<sf
->mm
->instance_count
; ++j
) {
460 free(sf
->mm
->instances
[j
]->origname
);
461 sf
->mm
->instances
[j
]->origname
= copy(norm
->origname
);
464 } else if ( !GFileExists(filename
) )
465 ff_post_error(_("Couldn't open font"),_("The requested file, %.100s, does not exist"),GFileNameTail(filename
));
466 else if ( !GFileReadable(filename
) )
467 ff_post_error(_("Couldn't open font"),_("You do not have permission to read %.100s"),GFileNameTail(filename
));
469 ff_post_error(_("Couldn't open font"),_("%.100s is not in a known format (or is so badly corrupted as to be unreadable)"),GFileNameTail(filename
));
471 if ( oldstrippedname
!=filename
)
472 free(oldstrippedname
);
473 if ( fullname
!=filename
&& fullname
!=strippedname
)
475 if ( tmpfile
!=NULL
) {
479 if ( (openflags
&of_fstypepermitted
) && sf
!=NULL
&& (sf
->pfminfo
.fstype
&0xff)==0x0002 ) {
480 /* Ok, they have told us from a script they have access to the font */
481 } else if ( !fromsfd
&& sf
!=NULL
&& (sf
->pfminfo
.fstype
&0xff)==0x0002 ) {
483 buts
[0] = _("_Yes"); buts
[1] = _("_No"); buts
[2] = NULL
;
484 if ( ff_ask(_("Restricted Font"),(const char **) buts
,1,1,_("This font is marked with an FSType of 2 (Restricted\nLicense). That means it is not editable without the\npermission of the legal owner.\n\nDo you have such permission?"))==1 ) {
492 SplineFont
*ReadSplineFont(char *filename
,enum openflags openflags
) {
493 return( _ReadSplineFont(NULL
,filename
,openflags
));
497 SplineFont
*ReadSplineFontInfo(char *filename
,enum openflags openflags
) {
498 SplineFont
*sf
, *sf_ptr
;
500 char *pt
=NULL
, *strippedname
=filename
, *paren
=NULL
, *rparen
=NULL
, *fullname
=filename
;
505 if ( filename
==NULL
)
508 pt
= strrchr(filename
,'/');
509 if ( pt
==NULL
) pt
= filename
;
510 /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
511 /* that ff wouldn't open it */
512 /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
513 if ( (paren
= strrchr(pt
,'('))!=NULL
&&
514 (rparen
= strrchr(paren
,')'))!=NULL
&&
516 strippedname
= copy(filename
);
517 strippedname
[paren
-filename
] = '\0';
521 foo
= fopen(strippedname
,"rb");
524 /* Try to guess the file type from the first few characters... */
530 if (( ch1
==0 && ch2
==1 && ch3
==0 && ch4
==0 ) ||
531 (ch1
=='O' && ch2
=='T' && ch3
=='T' && ch4
=='O') ||
532 (ch1
=='t' && ch2
=='r' && ch3
=='u' && ch4
=='e') ) {
533 sf
= SFReadTTFInfo(fullname
,0,openflags
);
535 } else if ((ch1
=='t' && ch2
=='t' && ch3
=='c' && ch4
=='f')) {
538 /* read all fonts in a collection */
539 fontlist
= NamesReadTTF(fullname
);
540 old_fontlist
= fontlist
;
542 while (*fontlist
!= NULL
) {
543 snprintf(s
,511, "%s(%s)", fullname
,*fontlist
);
544 sf_ptr
= SFReadTTFInfo(s
,0,openflags
);
550 /* fontlist is (g)allocated */
551 fontlist
= old_fontlist
;
552 for(i
=0; fontlist
[i
]; i
++)
557 } else if ( strmatch(fullname
+strlen(strippedname
)-4, ".bin")==0 ||
558 strmatch(fullname
+strlen(strippedname
)-4, ".hqx")==0 ||
559 strmatch(fullname
+strlen(strippedname
)-6, ".dfont")==0 ) {
560 fontlist
= NamesReadMacBinary(fullname
);
562 while (*fontlist
!= NULL
) {
563 snprintf(s
,511, "%s(%s)", fullname
,*fontlist
);
564 sf_ptr
= SFReadMacBinaryInfo(s
,0,openflags
);
572 sf
= ReadSplineFont (fullname
, openflags
);
575 if ( strippedname
!=filename
)
581 /* Use URW 4 letter abbreviations */
582 char *knownweights
[] = { "Demi", "Bold", "Regu", "Medi", "Book", "Thin",
583 "Ligh", "Heav", "Blac", "Ultr", "Nord", "Norm", "Gras", "Stan", "Halb",
584 "Fett", "Mage", "Mitt", "Buch", NULL
};
585 char *realweights
[] = { "Demi", "Bold", "Regular", "Medium", "Book", "Thin",
586 "Light", "Heavy", "Black", "Ultra", "Nord", "Normal", "Gras", "Standard", "Halbfett",
587 "Fett", "Mager", "Mittel", "Buchschrift", NULL
};
588 static char *moreweights
[] = { "ExtraLight", "VeryLight", NULL
};
589 char **noticeweights
[] = { moreweights
, realweights
, knownweights
, NULL
};
591 static char *modifierlist
[] = { "Ital", "Obli", "Kursive", "Cursive", "Slanted",
592 "Expa", "Cond", NULL
};
593 static char *modifierlistfull
[] = { "Italic", "Oblique", "Kursive", "Cursive", "Slanted",
594 "Expanded", "Condensed", NULL
};
595 static char **mods
[] = { knownweights
, modifierlist
, NULL
};
596 static char **fullmods
[] = { realweights
, modifierlistfull
, NULL
};
598 char *_GetModifiers(char *fontname
, char *familyname
,char *weight
) {
602 /* URW fontnames don't match the familyname */
603 /* "NimbusSanL-Regu" vs "Nimbus Sans L" (note "San" vs "Sans") */
604 /* so look for a '-' if there is one and use that as the break point... */
606 if ( (fpt
=strchr(fontname
,'-'))!=NULL
) {
610 } else if ( familyname
!=NULL
) {
611 for ( pt
= fontname
, fpt
=familyname
; *fpt
!='\0' && *pt
!='\0'; ) {
614 } else if ( *fpt
==' ' )
618 else if ( *fpt
=='a' || *fpt
=='e' || *fpt
=='i' || *fpt
=='o' || *fpt
=='u' )
619 ++fpt
; /* allow vowels to be omitted from family when in fontname */
623 if ( *fpt
=='\0' && *pt
!='\0' )
630 for ( i
=0; mods
[i
]!=NULL
; ++i
) for ( j
=0; mods
[i
][j
]!=NULL
; ++j
) {
631 pt
= strstr(fontname
,mods
[i
][j
]);
632 if ( pt
!=NULL
&& (fpt
==NULL
|| pt
<fpt
))
637 for ( i
=0; mods
[i
]!=NULL
; ++i
) for ( j
=0; mods
[i
][j
]!=NULL
; ++j
) {
638 if ( strcmp(fpt
,mods
[i
][j
])==0 )
639 return( fullmods
[i
][j
]);
641 if ( strcmp(fpt
,"BoldItal")==0 )
642 return( "BoldItalic" );
643 else if ( strcmp(fpt
,"BoldObli")==0 )
644 return( "BoldOblique" );
649 return( weight
==NULL
|| *weight
=='\0' ? "Regular": weight
);
652 char *SFGetModifiers(SplineFont
*sf
) {
653 return( _GetModifiers(sf
->fontname
,sf
->familyname
,sf
->weight
));