diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettf.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettf.c | 6489 |
1 files changed, 6489 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettf.c b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettf.c new file mode 100644 index 00000000000..1e1cf2ccad5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/parsettf.c @@ -0,0 +1,6489 @@ +/* Copyright (C) 2000-2008 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 "pfaedit.h" +#include <chardata.h> +#include <utype.h> +#include <ustring.h> +#include <math.h> +#include <locale.h> +#include <gwidget.h> +#include "ttf.h" + +#ifdef LUA_FF_LIB +SplineFont *_SFReadTTFInfo(FILE *ttf, int flags,enum openflags openflags, char *filename,struct fontdict *fd); +void THPatchSplineChar(SplineChar *sc); +#endif + +char *SaveTablesPref; +int ask_user_for_cmap = false; + +/* True Type is a really icky format. Nothing is together. It's badly described */ +/* much of the description is misleading */ +/* Apple's version: */ +/* http://fonts.apple.com/TTRefMan/index.html */ +/* MS's version: */ +/* http://www.microsoft.com/typography/tt/tt.htm */ +/* An helpful but incomplete description is given at */ +/* http://www.truetype.demon.co.uk/ttoutln.htm */ +/* For some things I looked at freetype's code to see how they did it */ +/* (I think only for what happens if !ARGS_ARE_XY) */ +/* http://freetype.sourceforge.net/ */ +/* It grows on you though... now that I understand it better it seems better designed */ +/* but the docs remain in conflict. Sometimes badly so */ + +int prefer_cjk_encodings=false; + +/* ************************************************************************** */ +static struct ms_2_locales { char *loc_name; int local_id; } ms_2_locals[] = { + { "af", 0x436 }, + { "sq_AL", 0x41c }, + { "am", 0x45e }, + { "ar_SA", 0x401 }, + { "ar_IQ", 0x801 }, + { "ar_EG", 0xc01 }, + { "ar_LY", 0x1001 }, + { "ar_DZ", 0x1401 }, + { "ar_MA", 0x1801 }, + { "ar_TN", 0x1C01 }, + { "ar_OM", 0x2001 }, + { "ar_YE", 0x2401 }, + { "ar_SY", 0x2801 }, + { "ar_JO", 0x2c01 }, + { "ar_LB", 0x3001 }, + { "ar_KW", 0x3401 }, + { "ar_AE", 0x3801 }, + { "ar_BH", 0x3c01 }, + { "ar_QA", 0x4001 }, + { "hy", 0x42b }, + { "as", 0x44d }, + { "az", 0x42c }, + { "az", 0x82c }, + { "eu", 0x42d }, + { "be_BY", 0x423 }, + { "bn_IN", 0x445 }, + { "bn_BD", 0x845 }, + { "bg_BG", 0x402 }, + { "my", 0x455 }, + { "ca", 0x403 }, + { "km", 0x453 }, + { "zh_TW", 0x404 }, /* Trad */ + { "zh_CN", 0x804 }, /* Simp */ + { "zh_HK", 0xc04 }, /* Trad */ + { "zh_SG", 0x1004 }, /* Simp */ + { "zh_MO", 0x1404 }, /* Trad */ + { "hr", 0x41a }, + { "hr_BA", 0x101a }, + { "cs_CZ", 0x405 }, + { "da_DK", 0x406 }, + { "div", 0x465 }, + { "nl_NL", 0x413 }, + { "nl_BE", 0x813 }, + { "en_UK", 0x809 }, + { "en_US", 0x409 }, + { "en_CA", 0x1009 }, + { "en_AU", 0xc09 }, + { "en_NZ", 0x1409 }, + { "en_IE", 0x1809 }, + { "en_ZA", 0x1c09 }, + { "en_JM", 0x2009 }, + { "en", 0x2409 }, + { "en_BZ", 0x2809 }, + { "en_TT", 0x2c09 }, + { "en_ZW", 0x3009 }, + { "en_PH", 0x3409 }, + { "en_ID", 0x3809 }, + { "en_HK", 0x3c09 }, + { "en_IN", 0x4009 }, + { "en_MY", 0x4409 }, + { "et_EE", 0x425 }, + { "fo", 0x438 }, +/* No language code for filipino */ + { "fa", 0x429 }, + { "fi_FI", 0x40b }, + { "fr_FR", 0x40c }, + { "fr_BE", 0x80c }, + { "fr_CA", 0xc0c }, + { "fr_CH", 0x100c }, + { "fr_LU", 0x140c }, + { "fr_MC", 0x180c }, + { "fr", 0x1c0c }, /* West Indes */ + { "fr_RE", 0x200c }, + { "fr_CD", 0x240c }, + { "fr_SN", 0x280c }, + { "fr_CM", 0x2c0c }, + { "fr_CI", 0x300c }, + { "fr_ML", 0x340c }, + { "fr_MA", 0x380c }, + { "fr_HT", 0x3c0c }, + { "fr_DZ", 0xe40c }, /* North African is most likely to be Algeria, possibly Tunisia */ + { "fy", 0x462 }, + { "gl", 0x456 }, + { "ka", 0x437 }, + { "de_DE", 0x407 }, + { "de_CH", 0x807 }, + { "de_AT", 0xc07 }, + { "de_LU", 0x1007 }, + { "de_LI", 0x1407 }, + { "el_GR", 0x408 }, + { "ga", 0x83c }, + { "gd", 0x43c }, + { "gn", 0x474 }, + { "gu", 0x447 }, + { "ha", 0x468 }, + { "he_IL", 0x40d }, + { "iw", 0x40d }, /* Obsolete name for Hebrew */ + { "hi", 0x439 }, + { "hu_HU", 0x40e }, + { "is_IS", 0x40f }, + { "id", 0x421 }, + { "in", 0x421 }, /* Obsolete name for Indonesean */ + { "iu", 0x45d }, + { "it_IT", 0x410 }, + { "it_CH", 0x810 }, + { "ja_JP", 0x411 }, + { "kn", 0x44b }, + { "ks_IN", 0x860 }, + { "kk", 0x43f }, + { "ky", 0x440 }, + { "km", 0x453 }, + { "kok", 0x457 }, + { "ko", 0x412 }, + { "ko", 0x812 }, /*Johab */ + { "lo", 0x454 }, + { "la", 0x476 }, + { "lv_LV", 0x426 }, + { "lt_LT", 0x427 }, + { "lt", 0x827 }, /* Classic */ + { "mk", 0x42f }, + { "ms", 0x43e }, + { "ms", 0x83e }, + { "ml", 0x44c }, + { "mt", 0x43a }, + { "mr", 0x44e }, + { "mn", 0x450 }, + { "ne_NP", 0x461 }, + { "ne_IN", 0x861 }, + { "no_NO", 0x414 }, /* Bokmal */ + { "no_NO", 0x814 }, /* Nynorsk */ + { "or", 0x448 }, + { "om", 0x472 }, + { "ps", 0x463 }, + { "pl_PL", 0x415 }, + { "pt_PT", 0x416 }, + { "pt_BR", 0x816 }, + { "pa_IN", 0x446 }, + { "pa_PK", 0x846 }, + { "qu_BO", 0x46b }, + { "qu_EC", 0x86b }, + { "qu_PE", 0xc6b }, + { "rm", 0x417 }, + { "ro_RO", 0x418 }, + { "ro_MD", 0x818 }, + { "ru_RU", 0x419 }, + { "ru_MD", 0x819 }, + { "smi", 0x43b }, + { "sa", 0x43b }, +/* No language code for Sepedi */ + { "sr", 0xc1a }, /* Cyrillic */ + { "sr", 0x81a }, /* Latin */ + { "sd_IN", 0x459 }, + { "sd_PK", 0x859 }, + { "si", 0x45b }, + { "sk_SK", 0x41b }, + { "sl_SI", 0x424 }, + { "wen", 0x42e }, + { "es_ES", 0x40a }, /* traditional spanish */ + { "es_MX", 0x80a }, + { "es_ES", 0xc0a }, /* Modern spanish */ + { "es_GT", 0x100a }, + { "es_CR", 0x140a }, + { "es_PA", 0x180a }, + { "es_DO", 0x1c0a }, + { "es_VE", 0x200a }, + { "es_CO", 0x240a }, + { "es_PE", 0x280a }, + { "es_AR", 0x2c0a }, + { "es_EC", 0x300a }, + { "es_CL", 0x340a }, + { "es_UY", 0x380a }, + { "es_PY", 0x3c0a }, + { "es_BO", 0x400a }, + { "es_SV", 0x440a }, + { "es_HN", 0x480a }, + { "es_NI", 0x4c0a }, + { "es_PR", 0x500a }, + { "es_US", 0x540a }, + { "sutu", 0x430 }, + { "sw_KE", 0x441 }, + { "sv_SE", 0x41d }, + { "sv_FI", 0x81d }, + { "tl", 0x464 }, + { "tg", 0x464 }, + { "ta", 0x449 }, + { "tt", 0x444 }, + { "te", 0x44a }, + { "th", 0x41e }, + { "bo_CN", 0x451 }, + { "bo_BT", 0x451 }, + { "ti_ET", 0x473 }, + { "ti_ER", 0x873 }, + { "ts", 0x431 }, + { "tn", 0x432 }, + { "tr_TR", 0x41f }, + { "tk", 0x442 }, + { "uk_UA", 0x422 }, + { "ug", 0x480 }, + { "ur_PK", 0x420 }, + { "ur_IN", 0x820 }, + { "uz", 0x443 }, /* Latin */ + { "uz", 0x843 }, /* Cyrillic */ + { "ven", 0x433 }, + { "vi", 0x42a }, + { "cy", 0x452 }, + { "xh", 0x434 }, + { "yi", 0x43d }, + { "ji", 0x43d }, /* Obsolete Yiddish */ + { "yo", 0x46a }, + { "zu", 0x435 }, + { NULL, 0 }}; + +int MSLanguageFromLocale(void) { + const char *lang=NULL; + int i, langlen; + static char *envs[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL }; + char langcountry[8], language[4]; + int langcode, langlocalecode; + + for ( i=0; envs[i]!=NULL; ++i ) { + lang = getenv(envs[i]); + if ( lang!=NULL ) { + langlen = strlen(lang); + if (( langlen>5 && lang[5]=='.' && lang[2]=='_' ) || + (langlen==5 && lang[2]=='_' ) || + (langlen==2) || + (langlen==3)) /* Some obscure languages have a 3 letter code */ + /* I understand this language */ + break; + } + } + if ( lang==NULL ) + lang = "en_US"; + strncpy(langcountry,lang,5); langcountry[5] = '\0'; + strncpy(language,lang,3); language[3] = '\0'; + if ( language[2]=='_' ) language[2] = '\0'; + langlen = strlen(language); + + langcode = langlocalecode = -1; + for ( i=0; ms_2_locals[i].loc_name!=NULL; ++i ) { + if ( strmatch(langcountry,ms_2_locals[i].loc_name)==0 ) { + langlocalecode = ms_2_locals[i].local_id; + langcode = langlocalecode&0x3ff; + break; + } else if ( strncmp(language,ms_2_locals[i].loc_name,langlen)==0 ) + langcode = ms_2_locals[i].local_id&0x3ff; + } + if ( langcode==-1 ) /* Default to English */ + langcode = 0x9; +return( langlocalecode==-1 ? (langcode|0x400) : langlocalecode ); +} +/* ************************************************************************** */ + +int getushort(FILE *ttf) { + int ch1 = getc(ttf); + int ch2 = getc(ttf); + if ( ch2==EOF ) +return( EOF ); +return( (ch1<<8)|ch2 ); +} + +int get3byte(FILE *ttf) { + int ch1 = getc(ttf); + int ch2 = getc(ttf); + int ch3 = getc(ttf); + if ( ch3==EOF ) +return( EOF ); +return( (ch1<<16)|(ch2<<8)|ch3 ); +} + +int32 getlong(FILE *ttf) { + int ch1 = getc(ttf); + int ch2 = getc(ttf); + int ch3 = getc(ttf); + int ch4 = getc(ttf); + if ( ch4==EOF ) +return( EOF ); +return( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 ); +} + +static int32 getoffset(FILE *ttf, int offsize) { + if ( offsize==1 ) +return( getc(ttf)); + else if ( offsize==2 ) +return( getushort(ttf)); + else if ( offsize==3 ) +return( get3byte(ttf)); + else +return( getlong(ttf)); +} + +real getfixed(FILE *ttf) { + int32 val = getlong(ttf); + int mant = val&0xffff; + /* This oddity may be needed to deal with the first 16 bits being signed */ + /* and the low-order bits unsigned */ +return( (real) (val>>16) + (mant/65536.0) ); +} + +real get2dot14(FILE *ttf) { + int32 val = getushort(ttf); + int mant = val&0x3fff; + /* This oddity may be needed to deal with the first 2 bits being signed */ + /* and the low-order bits unsigned */ +return( (real) ((val<<16)>>(16+14)) + (mant/16384.0) ); +} + +static Encoding *enc_from_platspec(int platform,int specific) { + char *enc; + Encoding *e; + + enc = "Custom"; + if ( platform==0 ) { + enc = "Unicode"; + if ( specific==4 ) + enc = "UnicodeFull"; + } else if ( platform==1 ) { + if ( specific==0 ) + enc = "Mac"; + else if ( specific==1 ) + enc = "Sjis"; + else if ( specific==2 ) + enc = "Big5hkscs"; /* Or should we just guess big5? Both are wrong sometimes */ + else if ( specific==3 ) + enc = "EUC-KR"; + else if ( specific==25 ) + enc = "EUC-CN"; + } else if ( platform==2 ) { /* obselete */ + if ( specific==0 ) + enc = "ASCII"; + else if ( specific==1 ) + enc = "Unicode"; + else if ( specific==2 ) + enc = "ISO8859-1"; + } else if ( platform==3 ) { + if ( specific==1 || specific==0 ) /* symbol (sp=0) is just unicode (PUA) */ + enc = "Unicode"; + else if ( specific==2 ) + enc = "Sjis"; + else if ( specific==3 ) + enc = "EUC-CN"; + else if ( specific==4 ) + enc = "Big5hkscs"; + else if ( specific==5 ) + enc = "EUC-KR"; + else if ( specific==6 ) + enc = "Johab"; + else if ( specific==10 ) + enc = "UnicodeFull"; + } else if ( platform==7 ) { /* Used internally in freetype, but */ + if ( specific==0 ) /* there's no harm in looking for it */ + enc = "AdobeStandard"; /* even if it never happens */ + else if ( specific==1 ) { + /* adobe_expert */; + } else if ( specific==2 ) { + /* adobe_custom */; + } + } + e = FindOrMakeEncoding(enc); + if ( e==NULL ) { + static int p = -1,s = -1; + if ( p!=platform || s!=specific ) { + LogError( _("The truetype encoding specified by platform=%d specific=%d (which we map to %s) is not supported by your version of iconv(3).\n"), + platform, specific, enc ); + p = platform; s = specific; + } + } +return( e ); +} + +static char *_readencstring(FILE *ttf,int offset,int len, + int platform,int specific,int language) { + long pos = ftell(ttf); + unichar_t *str, *pt; + char *ret; + int i, ch; + Encoding *enc; + + fseek(ttf,offset,SEEK_SET); + + if ( platform==1 ) { + /* Mac is screwy, there are several different varients of MacRoman */ + /* depending on the language, they didn't get it right when they */ + /* invented their script system */ + char *cstr, *cpt; + cstr = cpt = galloc(len+1); + for ( i=0; i<len; ++i ) + *cpt++ = getc(ttf); + *cpt = '\0'; + ret = MacStrToUtf8(cstr,specific,language); + free(cstr); + } else { + enc = enc_from_platspec(platform,specific); + if ( enc==NULL ) +return( NULL ); + if ( enc->is_unicodebmp ) { + str = pt = galloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t)); + for ( i=0; i<len/2; ++i ) { + ch = getc(ttf)<<8; + *pt++ = ch | getc(ttf); + } + *pt = 0; + } else if ( enc->unicode!=NULL ) { + str = pt = galloc(sizeof(unichar_t)*len+sizeof(unichar_t)); + for ( i=0; i<len; ++i ) + *pt++ = enc->unicode[getc(ttf)]; + *pt = 0; + } else if ( enc->tounicode!=NULL ) { + size_t inlen = len+1, outlen = sizeof(unichar_t)*(len+1); + char *cstr = galloc(inlen); + ICONV_CONST char *in = cstr; + char *out; + str = galloc(outlen+2); + out = (char *) str; + iconv(enc->tounicode,&in,&inlen,&out,&outlen); + out[0] = '\0'; out[1] = '\0'; +#ifndef UNICHAR_16 + out[2] = '\0'; out[3] = '\0'; +#endif + free(cstr); + } else { + str = uc_copy(""); + } + ret = u2utf8_copy(str); + free(str); + } + fseek(ttf,pos,SEEK_SET); +return( ret ); +} + +char *TTFGetFontName(FILE *ttf,int32 offset,int32 off2) { + int i,num; + int32 tag, length, stringoffset; + int plat, spec, lang, name, len, off, val; + Encoding *enc; + int32 nameoffset = 0; + int fullval=0, fullstr=0, fulllen=0, famval=0, famstr=0, famlen=0; + int fullplat=0, fullspec=0, fulllang=0, famplat=0, famspec=0, famlang=0; + int locale = MSLanguageFromLocale(); + int maclang = WinLangToMac(locale); + + fseek(ttf,offset,SEEK_SET); + /* version = */ getlong(ttf); + num = getushort(ttf); + /* srange = */ getushort(ttf); + /* esel = */ getushort(ttf); + /* rshift = */ getushort(ttf); + for ( i=0; i<num; ++i ) { + tag = getlong(ttf); + /* checksum = */ getlong(ttf); + nameoffset = off2+getlong(ttf); + length = getlong(ttf); + if ( tag==CHR('n','a','m','e')) + break; + } + if ( i==num ) +return( NULL ); + + fseek(ttf,nameoffset,SEEK_SET); + /* format = */ getushort(ttf); + num = getushort(ttf); + stringoffset = nameoffset+getushort(ttf); + fullval = famval = 0; + for ( i=0; i<num; ++i ) { + plat = getushort(ttf); + spec = getushort(ttf); + lang = getushort(ttf); + name = getushort(ttf); + len = getushort(ttf); + off = getushort(ttf); + enc = enc_from_platspec(plat,spec); + if ( enc==NULL ) + continue; + val = 0; + if ( plat==3 && !enc->is_custom && lang==locale ) + val = 15; + else if ( plat==3 && !enc->is_custom && (lang&0xff)==(locale&0xff) ) + val = 14; + else if ( (plat==0 || plat==1) && !enc->is_custom && lang==maclang ) + val = 13; + /* Ok, that didn't work, how about an english name? */ + else if ( plat==3 && !enc->is_custom && lang==0x409 ) + val = 12; + else if ( plat==3 && !enc->is_custom && (lang&0xff)==0x09 ) + val = 11; + else if ( (plat==0 || plat==1) && !enc->is_custom && lang==0 ) + val = 10; + /* failing that I'll take what I can get */ + else if ( !enc->is_custom ) + val = 1; + if ( name==4 && val>fullval ) { + fullval = val; + fullstr = off; + fulllen = len; + fullplat = plat; + fullspec = spec; + fulllang = lang; + if ( val==12 ) + break; + } else if ( name==1 && val>famval ) { + famval = val; + famstr = off; + famlen = len; + famplat = plat; + famspec = spec; + famlang = lang; + } + } + if ( fullval==0 ) { + if ( famval==0 ) +return( NULL ); + fullstr = famstr; + fulllen = famlen; + fullplat = famplat; + fullspec = famspec; + fulllang = famlang; + } +return( _readencstring(ttf,stringoffset+fullstr,fulllen,fullplat,fullspec,fulllang)); +} + +static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) { + int32 *offsets, cnt, i, choice, j; + char **names; + char *pt, *lparen, *rparen; + + /* TTCF version = */ getlong(ttf); + cnt = getlong(ttf); + if ( cnt==1 ) { + /* This is easy, don't bother to ask the user, there's no choice */ + int32 offset = getlong(ttf); + fseek(ttf,offset,SEEK_SET); + return( true ); + } + offsets = galloc(cnt*sizeof(int32)); + for ( i=0; i<cnt; ++i ) + offsets[i] = getlong(ttf); + names = galloc(cnt*sizeof(char *)); + for ( i=j=0; i<cnt; ++i ) { + names[j] = TTFGetFontName(ttf,offsets[i],0); + if ( names[j]!=NULL ) ++j; + } + pt = strrchr(filename,'/'); + if ( pt==NULL ) pt = filename; + /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */ + /* that ff wouldn't open it */ + /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */ + if ( (lparen = strrchr(pt,'('))!=NULL && + (rparen = strrchr(lparen,')'))!=NULL && + rparen[1]=='\0' ) { + char *find = copy(lparen+1); + pt = strchr(find,')'); + if ( pt!=NULL ) *pt='\0'; + for ( choice=cnt-1; choice>=0; --choice ) + if ( strcmp(names[choice],find)==0 ) + break; + if ( choice==-1 ) { + char *end; + choice = strtol(find,&end,10); + if ( *end!='\0' ) + choice = -1; + } + if ( choice==-1 ) { + char *fn = copy(filename); + fn[lparen-filename] = '\0'; + ff_post_error(_("Not in Collection"), +/* GT: The user is trying to open a font file which contains multiple fonts and */ +/* GT: has asked for a font which is not in that file. */ +/* GT: The string will look like: <fontname> is not in <filename> */ + _("%1$s is not in %2$.100s"),find,fn); + free(fn); + } + free(find); + } else + choice = 0; + if ( choice!=-1 ) { + fseek(ttf,offsets[choice],SEEK_SET); + *chosenname = copy(names[choice]); + } + for ( i=0; i<j; ++i ) + free(names[i]); + free(names); + free(offsets); +return( choice!=-1); +} + +static int PickCFFFont(char **fontnames) { + unichar_t **names; + int cnt, i, choice; + + for ( cnt=0; fontnames[cnt]!=NULL; ++cnt); + names = gcalloc(cnt+1,sizeof(unichar_t *)); + for ( i=0; i<cnt; ++i ) + names[i] = uc_copy(fontnames[i]); + choice = 0; + for ( i=0; i<cnt; ++i ) + free(names[i]); + free(names); +return( choice ); +} + +static void ParseSaveTablesPref(struct ttfinfo *info) { + extern char *SaveTablesPref; + char *pt, *spt; + int cnt; + + info->savecnt = 0; + info->savetab = NULL; + if ( SaveTablesPref==NULL || *SaveTablesPref=='\0' ) +return; + for ( pt=SaveTablesPref, cnt=0; *pt; ++pt ) + if ( *pt==',' ) + ++cnt; + info->savecnt = cnt+1; + info->savetab = gcalloc(cnt+1,sizeof(struct savetab)); + for ( pt=spt=SaveTablesPref, cnt=0; ; ++pt ) { + if ( *pt==',' || *pt=='\0' ) { + uint32 tag; + tag = ( ( spt <pt )? spt[0] : ' ' )<<24; + tag |= ( ( spt+1<pt )? spt[1] : ' ' )<<16; + tag |= ( ( spt+2<pt )? spt[2] : ' ' )<<8 ; + tag |= ( ( spt+3<pt )? spt[3] : ' ' ) ; + info->savetab[cnt++].tag = tag; + if ( *pt ) + spt = pt+1; + else + break; + } + } +} + +static int32 filechecksum(FILE *file, int start, int len) { + uint32 sum = 0, chunk; + + fseek(file,start,SEEK_SET); + if ( len!=-1 ) len=(len+3)>>2; + while ( len==-1 || --len>=0 ) { + chunk = getlong(file); + if ( feof(file)) + break; + sum += chunk; + } +return( sum ); +} + +static void ValidateTTFHead(FILE *ttf,struct ttfinfo *info) { + /* When doing font lint we want to check the ttf header and make */ + /* sure all the offsets and lengths are valid, and the checksums */ + /* match. Most of the time this is just extra work and we don't */ + /* bather */ + uint32 restore_this_pos = ftell(ttf); + struct tt_tables { + uint32 tag; + uint32 checksum; + uint32 offset; + uint32 length; + } *tabs, temp; + int i,j; + uint32 file_len; + int sr, es, rs, e_sr, e_es, e_rs; + int hashead, hashhea, hasmaxp, masos2, haspost, hasname, hasos2; + int hasloca, hascff, hasglyf; + + info->numtables = getushort(ttf); + sr = getushort(ttf); + es = getushort(ttf); + rs = getushort(ttf); + e_sr = (info->numtables<8?4:info->numtables<16?8:info->numtables<32?16:info->numtables<64?32:64)*16; + e_es = (info->numtables<8?2:info->numtables<16?3:info->numtables<32?4:info->numtables<64?5:6); + e_rs = info->numtables*16-e_sr; + if ( e_sr!=sr || e_es!=es || e_rs!=rs ) { + LogError( _("Unexpected values for binsearch header. Based on the number of tables I\n expect searchRange=%d (not %d), entrySel=%d (not %d) rangeShift=%d (not %d)\n"), + e_sr, sr, e_es, es, e_rs, rs ); + info->bad_sfnt_header = true; + } + + if ( info->numtables<=0 ) { + LogError(_("An sfnt file must contain SOME tables, but this one does not.")); + info->bad_sfnt_header = true; + fseek(ttf,restore_this_pos,SEEK_SET); +return; + } else if ( info->numtables>1000 ) { + LogError(_("An sfnt file may contain a large number of tables, but this one has over 1000\n and that seems like too many\n")); + info->bad_sfnt_header = true; + fseek(ttf,restore_this_pos,SEEK_SET); +return; + } + + tabs = galloc(info->numtables*sizeof(struct tt_tables)); + + for ( i=0; i<info->numtables; ++i ) { + tabs[i].tag = getlong(ttf); + tabs[i].checksum = getlong(ttf); + tabs[i].offset = getlong(ttf); + tabs[i].length = getlong(ttf); + if ( i!=0 && tabs[i].tag<tabs[i-1].tag && !info->bad_sfnt_header ) { + LogError(_("Table tags should be in alphabetic order in the font header\n but '%c%c%c%c', appears after '%c%c%c%c'."), + tabs[i-1].tag>>24, tabs[i-1].tag>>16, tabs[i-1].tag>>8, tabs[i-1].tag, + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag ); + info->bad_sfnt_header = true; + } + } + fseek(ttf,0,SEEK_END); + file_len = ftell(ttf); + + for ( i=0; i<info->numtables; ++i ) for ( j=i+1; j<info->numtables; ++j ) { + if ( tabs[i].offset>tabs[j].offset ) { + temp = tabs[i]; + tabs[i] = tabs[j]; + tabs[j] = temp; + } + } + for ( i=0; i<info->numtables-1; ++i ) { + for ( j=i+1; j<info->numtables; ++j ) { + if ( tabs[i].tag==tabs[j].tag ) { + LogError(_("Same table tag, '%c%c%c%c', appears twice in sfnt header"), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag ); + info->bad_sfnt_header = true; + } + } + if ( tabs[i].offset+tabs[i].length > tabs[i+1].offset ) { + LogError(_("Tables '%c%c%c%c' and '%c%c%c%c' overlap"), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag, + tabs[j].tag>>24, tabs[j].tag>>16, tabs[j].tag>>8, tabs[j].tag ); + } + } + if ( tabs[i].offset+tabs[i].length > file_len ) { + LogError(_("Table '%c%c%c%c' extends beyond end of file."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag ); + info->bad_sfnt_header = true; + } + + /* Checksums. First file as a whole, then each table */ + if ( filechecksum(ttf,0,-1)!=(int)0xb1b0afba ) { + LogError(_("File checksum is incorrect.")); + info->bad_sfnt_header = true; + } + for ( i=0; i<info->numtables-1; ++i ) if ( tabs[i].tag!=CHR('h','e','a','d')) { + if ( filechecksum(ttf,tabs[i].offset,tabs[i].length)!=(int)tabs[i].checksum ) { + LogError(_("Table '%c%c%c%c' has a bad checksum."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag ); + info->bad_sfnt_header = true; + } + } + + hashead = hashhea = hasmaxp = masos2 = haspost = hasname = hasos2 = false; + hasloca = hascff = hasglyf = false; + for ( i=0; i<info->numtables-1; ++i ) { + switch ( tabs[i].tag ) { + case CHR('c','v','t',' '): + if ( tabs[i].length&1 ) + LogError(_("Table '%c%c%c%c' has a bad length, must be even."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag ); + break; + case CHR('b','h','e','d'): /* Fonts with bitmaps but no outlines get bhea */ + case CHR('h','e','a','d'): + if ( tabs[i].length!=54 ) + LogError(_("Table '%c%c%c%c' has a bad length, must be 54 but is %d."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag, + tabs[i].length ); + hashead = true; + break; + case CHR('h','h','e','a'): + hashhea = true; + case CHR('v','h','e','a'): + if ( tabs[i].length!=36 ) + LogError(_("Table '%c%c%c%c' has a bad length, must be 36 but is %d."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag, + tabs[i].length ); + break; + case CHR('m','a','x','p'): + hasmaxp = true; + if ( tabs[i].length!=32 && tabs[i].length!=6 ) + LogError(_("Table '%c%c%c%c' has a bad length, must be 32 or 6 but is %d."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag, + tabs[i].length ); + break; + case CHR('O','S','/','2'): + hasos2 = true; + if ( tabs[i].length!=78 && tabs[i].length!=86 && tabs[i].length!=96 ) + LogError(_("Table '%c%c%c%c' has a bad length, must be 78, 86 or 96 but is %d."), + tabs[i].tag>>24, tabs[i].tag>>16, tabs[i].tag>>8, tabs[i].tag, + tabs[i].length ); + break; + case CHR('p','o','s','t'): + haspost = true; + break; + case CHR('n','a','m','e'): + hasname = true; + break; + case CHR('l','o','c','a'): + hasloca = true; + break; + case CHR('g','l','y','f'): + hasglyf = true; + break; + case CHR('C','F','F',' '): + hascff = true; + break; + } + } + if ( !hashead ) + LogError(_("Missing required table: \"head\"")); + if ( !hashhea ) + LogError(_("Missing required table: \"hhea\"")); + if ( !hasmaxp ) + LogError(_("Missing required table: \"maxp\"")); + if ( !haspost ) + LogError(_("Missing required table: \"post\"")); + if ( !hasname ) + LogError(_("Missing required table: \"name\"")); + if ( hasglyf && !hasloca ) + LogError(_("Missing required table: \"loca\"")); + if ( !hasos2 ) + LogError(_("Missing \"OS/2\" table")); + if ( !hasglyf && hasloca ) + LogError(_("Missing required table: \"glyf\"")); + if ( !hasglyf && !hascff ) + LogError(_("This font contains neither \"CFF \" nor \"glyf\"/\"loca\" tables")); + + free(tabs); + fseek(ttf,restore_this_pos,SEEK_SET); +} + +static struct tablenames { uint32 tag; char *name; } stdtables[] = { + { CHR('a','c','n','t'), N_("accent attachment table") }, + { CHR('a','v','a','r'), N_("axis variation table") }, + { CHR('B','A','S','E'), N_("Baseline table (OT version)") }, + { CHR('b','d','a','t'), N_("bitmap data table (AAT version)") }, + { CHR('B','D','F',' '), N_("BDF bitmap properties table") }, + { CHR('b','h','e','d'), N_("bitmap font header table") }, + { CHR('b','l','o','c'), N_("bitmap location table (AAT version)") }, + { CHR('b','s','l','n'), N_("baseline table (AAT version)") }, + { CHR('C','F','F',' '), N_("PostScript font program (Compact Font Format)") }, + { CHR('C','I','D',' '), N_("Obsolete table for a type1 CID keyed font") }, + { CHR('c','m','a','p'), N_("character code mapping table") }, + { CHR('c','v','a','r'), N_("CVT variation table") }, + { CHR('c','v','t',' '), N_("control value table") }, + { CHR('D','S','I','G'), N_("digital signature table") }, + { CHR('E','B','D','T'), N_("bitmap data table (OT version)") }, + { CHR('E','B','L','C'), N_("bitmap location table (OT version)") }, + { CHR('E','B','S','C'), N_("embedded bitmap scaling control table") }, + { CHR('E','L','U','A'), N_("electronic end user license table") }, + { CHR('f','d','s','c'), N_("font descriptor table") }, + { CHR('f','e','a','t'), N_("layout feature table") }, + { CHR('F','e','a','t'), N_("SIL Graphite layout feature table") }, + { CHR('F','F','T','M'), N_("FontForge time stamp table") }, + { CHR('f','m','t','x'), N_("font metrics table") }, + { CHR('f','p','g','m'), N_("font program table") }, + { CHR('f','v','a','r'), N_("font variation table") }, + { CHR('g','a','s','p'), N_("grid-fitting and scan-conversion procedure table") }, + { CHR('G','D','E','F'), N_("glyph definition table") }, + { CHR('G','l','a','t'), N_("Graphite glyph attribute table") }, + { CHR('G','l','o','c'), N_("Graphite glyph location in Glat table") }, + { CHR('g','l','y','f'), N_("glyph outline table") }, + { CHR('G','P','O','S'), N_("glyph positioning table") }, + { CHR('g','v','a','r'), N_("glyph variation table") }, + { CHR('G','S','U','B'), N_("glyph substitution table") }, + { CHR('h','d','m','x'), N_("horizontal device metrics table") }, + { CHR('h','e','a','d'), N_("font header table") }, + { CHR('h','h','e','a'), N_("horizontal header table") }, + { CHR('h','m','t','x'), N_("horizontal metrics table") }, + { CHR('h','s','t','y'), N_("horizontal style table") }, + { CHR('j','u','s','t'), N_("justification table (AAT version)") }, + { CHR('J','S','T','F'), N_("justification table (OT version)") }, + { CHR('k','e','r','n'), N_("kerning table") }, + { CHR('l','c','a','r'), N_("ligature caret table") }, + { CHR('l','o','c','a'), N_("glyph location table") }, + { CHR('L','T','S','H'), N_("linear threshold table") }, + { CHR('M','A','T','H'), N_("math table") }, + { CHR('m','a','x','p'), N_("maximum profile table") }, + { CHR('M','M','S','D'), N_("Multi-Master table, obsolete") }, + { CHR('M','M','F','X'), N_("Multi-Master table, obsolete") }, + { CHR('m','o','r','t'), N_("metamorphosis table") }, + { CHR('m','o','r','x'), N_("extended metamorphosis table") }, + { CHR('n','a','m','e'), N_("name table") }, + { CHR('o','p','b','d'), N_("optical bounds table") }, + { CHR('O','S','/','2'), N_("OS/2 and Windows specific metrics table") }, + { CHR('P','C','L','T'), N_("PCL 5 data table") }, + { CHR('P','f','E','d'), N_("FontForge font debugging table") }, + { CHR('p','o','s','t'), N_("glyph name and PostScript compatibility table") }, + { CHR('p','r','e','p'), N_("control value program table") }, + { CHR('p','r','o','p'), N_("properties table") }, + { CHR('S','i','l','f'), N_("SIL Graphite rule table") }, + { CHR('S','i','l','l'), N_("(unspecified) SIL Graphite table") }, + { CHR('S','i','l','t'), N_("unknown SIL table") }, + { CHR('T','e','X',' '), N_("TeX table") }, + { CHR('t','r','a','k'), N_("tracking table") }, + { CHR('T','Y','P','1'), N_("Obsolete table for a type1 font") }, + { CHR('V','D','M','X'), N_("vertical device metrics table") }, + { CHR('v','h','e','a'), N_("vertical header table") }, + { CHR('v','m','t','x'), N_("vertical metrics table") }, + { CHR('V','O','R','G'), N_("vertical origin table") }, + { CHR('Z','a','p','f'), N_("glyph reference table") }, + { 0 , NULL } +}; + +static int readttfheader(FILE *ttf, struct ttfinfo *info,char *filename, + char **choosenname) { + int i, j, k; + unsigned tag; + int checksum, offset, length, version; + int first = true; + + version=getlong(ttf); + if ( version==CHR('t','t','c','f')) { + /* TrueType font collection */ + info->is_ttc = true; + if ( !PickTTFFont(ttf,filename,choosenname)) +return( 0 ); + /* If they picked a font, then we should be left pointing at the */ + /* start of the Table Directory for that font */ + info->one_of_many = true; + version = getlong(ttf); + } + + /* Apple says that 'typ1' is a valid code for a type1 font wrapped up in */ + /* a truetype table structure, but gives no docs on what tables get used */ + /* or how */ /* Turns out to be pretty simple */ + /* typ1 is used for both type1 fonts and CID type1 fonts, I don't think a version of 'CID ' is actually used */ + if ( version==CHR('t','y','p','1') || version==CHR('C','I','D',' ')) { + LogError( _("Nifty, you've got one of the old Apple/Adobe type1 sfnts here\n") ); + } else if ( version!=0x00010000 && version!=CHR('t','r','u','e') && + version!=0x00020000 && /* Windows 3.1 Chinese version used this version for some arphic fonts */ + /* See discussion on freetype list, july 2004 */ + version!=CHR('O','T','T','O')) +return( 0 ); /* Not version 1 of true type, nor Open Type */ + + if ( info->openflags & of_fontlint ) + ValidateTTFHead(ttf,info); + + info->numtables = getushort(ttf); + /* searchRange = */ getushort(ttf); + /* entrySelector = */ getushort(ttf); + /* rangeshift = */ getushort(ttf); + + ParseSaveTablesPref(info); + + for ( i=0; i<info->numtables; ++i ) { + tag = getlong(ttf); + checksum = getlong(ttf); + offset = getlong(ttf); + length = getlong(ttf); +#ifdef DEBUG + printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff ); +#endif + switch ( tag ) { + case CHR('B','A','S','E'): + info->base_start = offset; + break; + case CHR('b','s','l','n'): + info->bsln_start = offset; + break; + case CHR('C','F','F',' '): + info->cff_start = offset; + info->cff_length = length; + break; + case CHR('c','m','a','p'): + info->encoding_start = offset; + break; + case CHR('g','a','s','p'): + info->gasp_start = offset; + break; + case CHR('g','l','y','f'): + info->glyph_start = offset; + info->glyph_length = length; + break; + case CHR('G','D','E','F'): + info->gdef_start = offset; + info->gdef_length = length; + break; + case CHR('G','P','O','S'): + info->gpos_start = offset; + info->gpos_length = length; + break; + case CHR('G','S','U','B'): + info->gsub_start = offset; + info->gsub_length = length; + break; + case CHR('b','d','a','t'): /* Apple/MS use a different tag, but the same format. Great. */ + case CHR('E','B','D','T'): + info->bitmapdata_start = offset; + info->bitmapdata_length = length; + break; + case CHR('b','l','o','c'): /* Apple/MS use a different tag. Great. */ + case CHR('E','B','L','C'): + info->bitmaploc_start = offset; + info->bitmaploc_length = length; + break; + case CHR('b','h','e','d'): /* Apple uses bhed for fonts with only bitmaps */ + case CHR('h','e','a','d'): + info->head_start = offset; + break; + case CHR('h','h','e','a'): + info->hhea_start = offset; + break; + case CHR('h','m','t','x'): + info->hmetrics_start = offset; + break; + case CHR('k','e','r','n'): + info->kern_start = offset; + break; + case CHR('l','o','c','a'): + info->glyphlocations_start = offset; + info->loca_length = length; + info->glyph_cnt = length/2-1; /* the minus one is because there is one extra entry to give the length of the last glyph */ + if ( info->glyph_cnt<0 ) info->glyph_cnt = 0; + break; + case CHR('m','a','x','p'): + info->maxp_start = offset; + info->maxp_len = length; + break; + case CHR('n','a','m','e'): + info->copyright_start = offset; + break; + case CHR('p','o','s','t'): + info->postscript_start = offset; + break; + case CHR('O','S','/','2'): + info->os2_start = offset; + break; + case CHR('C','I','D',' '): + case CHR('T','Y','P','1'): + info->typ1_start = offset; + info->typ1_length = length; + break; + case CHR('v','h','e','a'): + info->vhea_start = offset; + break; + case CHR('v','m','t','x'): + info->vmetrics_start = offset; + break; + case CHR('M','A','T','H'): + info->math_start = offset; + info->math_length = length; + break; + /* Apple stuff */ +#if 0 + case CHR('a','c','n','t'): + info->acnt_start = offset; + break; +#endif + case CHR('f','e','a','t'): + info->feat_start = offset; + break; + case CHR('l','c','a','r'): + info->lcar_start = offset; + break; + case CHR('m','o','r','t'): + info->mort_start = offset; + break; + case CHR('m','o','r','x'): + info->morx_start = offset; + break; + case CHR('o','p','b','d'): + info->opbd_start = offset; + break; + case CHR('p','r','o','p'): + info->prop_start = offset; + break; + /* to make sense of instrs */ + case CHR('c','v','t',' '): + info->cvt_start = offset; + info->cvt_len = length; + break; + case CHR('p','r','e','p'): + info->prep_start = offset; + info->prep_len = length; + break; + case CHR('f','p','g','m'): + info->fpgm_start = offset; + info->fpgm_len = length; + break; + + /* non-standard tables I've added */ + case CHR('P','f','E','d'): + info->pfed_start = offset; + break; + case CHR('F','F','T','M'): + info->fftm_start = offset; + break; + case CHR('T','e','X',' '): + info->tex_start = offset; + break; + case CHR('B','D','F',' '): + info->bdf_start = offset; + break; + + /* Apple's mm fonts */ + case CHR('g','v','a','r'): + info->gvar_start = offset; + info->gvar_len = length; + break; + case CHR('f','v','a','r'): + info->fvar_start = offset; + info->fvar_len = length; + break; + case CHR('a','v','a','r'): + info->avar_start = offset; + info->avar_len = length; + break; + case CHR('c','v','a','r'): + info->cvar_start = offset; + info->cvar_len = length; + break; + + default: + for ( j=0; j<info->savecnt; ++j ) if ( info->savetab[j].tag == tag ) { + info->savetab[j].offset = offset; + info->savetab[j].len = length; + break; + } + if ( j==info->savecnt ) { + if ( first ) { + LogError( _("The following table(s) in the font have been ignored by FontForge\n") ); + first = false; + } + for ( k=0; stdtables[k].tag!=0; ++k ) + if ( stdtables[k].tag == tag ) + break; + if ( stdtables[k].tag==0 ) { + LogError( _(" Ignoring '%c%c%c%c'\n"), tag>>24, tag>>16, tag>>8, tag); + } else { + LogError( _(" Ignoring '%c%c%c%c' %s\n"), tag>>24, tag>>16, tag>>8, tag, + _(stdtables[k].name)); + } + } + } + } + if ( info->glyphlocations_start!=0 && info->cff_start!=0 ) + LogError( _("This font contains both truetype and PostScript glyph descriptions\n only one will be used.\n")); + else if ( (info->glyphlocations_start!=0) + + (info->cff_start!=0) + + (info->typ1_start!=0)>1 ) + LogError( _("This font contains multiple glyph descriptions\n only one will be used.\n")); + if ( info->gpos_start!=0 && info->kern_start!=0 ) + LogError( _("This font contains both a 'kern' table and a 'GPOS' table.\n The 'kern' table will only be read if there is no 'kern' feature in 'GPOS'.\n")); + if ( (info->mort_start!=0 || info->morx_start!=0) && info->gsub_start!=0 ) + LogError( _("This font contains both a 'mor[tx]' table and a 'GSUB' table.\n FF will only read feature/settings in 'morx' which do not match features\n found in 'GSUB'.\n")); + if ( info->base_start!=0 && info->bsln_start!=0 ) + LogError( _("This font contains both a 'BASE' table and a 'bsln' table.\n FontForge will only read one of them ('BASE').\n")); +return( true ); +} + +static void readdate(FILE *ttf,struct ttfinfo *info,int ismod) { + int date[4], date1970[4], year[2]; + int i; + /* Dates in sfnt files are seconds since 1904. I adjust to unix time */ + /* seconds since 1970 by figuring out how many seconds were in between */ + + date[3] = getushort(ttf); + date[2] = getushort(ttf); + date[1] = getushort(ttf); + date[0] = getushort(ttf); + memset(date1970,0,sizeof(date1970)); + year[0] = (60*60*24*365L)&0xffff; + year[1] = (60*60*24*365L)>>16; + for ( i=1904; i<1970; ++i ) { + date1970[0] += year[0]; + date1970[1] += year[1]; + if ( (i&3)==0 && (i%100!=0 || i%400==0)) + date1970[0] += 24*60*60L; /* Leap year */ + date1970[1] += (date1970[0]>>16); + date1970[0] &= 0xffff; + date1970[2] += date1970[1]>>16; + date1970[1] &= 0xffff; + date1970[3] += date1970[2]>>16; + date1970[2] &= 0xffff; + } + + for ( i=0; i<3; ++i ) { + date[i] -= date1970[i]; + date[i+1] += date[i]>>16; + date[i] &= 0xffff; + } + date[3] -= date1970[3]; + + *(ismod ? &info->modificationtime : &info->creationtime) = +#ifdef _HAS_LONGLONG + (((long long) date[3])<<48) | + (((long long) date[2])<<32) | +#endif + ( date[1] <<16) | + date[0]; +} + +static void readttfhead(FILE *ttf,struct ttfinfo *info) { + /* Here I want units per em, and size of loca entries */ + /* oh... also creation/modification times */ + int i, flags; + + fseek(ttf,info->head_start+4*4,SEEK_SET); /* skip over the version number and a bunch of junk */ + flags = getushort(ttf); + info->optimized_for_cleartype = (flags&(1<<13))?1:0; + info->apply_lsb = !(flags&(1<<1)); + info->emsize = getushort(ttf); + + info->ascent = .8*info->emsize; + info->descent = info->emsize-info->ascent; + + for ( i=0; i<12; ++i ) + getushort(ttf); + info->macstyle = getushort(ttf); + for ( i=0; i<2; ++i ) + getushort(ttf); + info->index_to_loc_is_long = getushort(ttf); + if ( info->index_to_loc_is_long ) + info->glyph_cnt = info->loca_length/4-1; + if ( info->glyph_cnt<0 ) info->glyph_cnt = 0; + + if ( info->fftm_start!=0 ) { + fseek(ttf,info->fftm_start+3*4,SEEK_SET); + } else { + fseek(ttf,info->head_start+4*4+2+2,SEEK_SET); + } + readdate(ttf,info,0); + readdate(ttf,info,1); +} + +static void readttfhhea(FILE *ttf,struct ttfinfo *info) { + /* Here I want ascent, descent and the number of horizontal metrics */ + int i; + + fseek(ttf,info->hhea_start+4,SEEK_SET); /* skip over the version number */ + info->pfminfo.hhead_ascent = getushort(ttf); + info->pfminfo.hhead_descent = (short) getushort(ttf); + info->pfminfo.hheadascent_add = info->pfminfo.hheaddescent_add = false; + info->pfminfo.linegap = getushort(ttf); + info->pfminfo.hheadset = true; + /*info->ascent = info->pfminfo.hhead_ascent;*/ + + for ( i=0; i<12; ++i ) + getushort(ttf); + info->width_cnt = getushort(ttf); +} + +static void readttfmaxp(FILE *ttf,struct ttfinfo *info) { + /* All I want here is the number of glyphs */ + int cnt; + fseek(ttf,info->maxp_start+4,SEEK_SET); /* skip over the version number */ + cnt = getushort(ttf); + if ( info->glyph_cnt==0 && info->glyph_length==0 && info->loca_length<=4 && + info->cff_length==0 && info->typ1_start==0 ) { + /* X11 OpenType bitmap format */; + info->onlystrikes = true; + } else if ( cnt!=info->glyph_cnt && info->loca_length!=0 ) { + ff_post_notice(_("Bad Glyph Count"), _("Font file has bad glyph count field. maxp says: %d sizeof(loca)=>%d"), cnt, info->glyph_cnt); + info->bad_glyph_data = true; + if ( cnt>info->glyph_cnt ) + cnt = info->glyph_cnt; /* Use the smaller of the two values */ + } + /* Open Type fonts have no loca table, so we can't calculate the glyph */ + /* count from it */ + info->glyph_cnt = cnt; + if ( cnt<0 ) info->glyph_cnt = 0; +} + +static char *stripspaces(char *str) { + char *str2 = str, *base = str; + + if ( str==NULL ) +return( NULL ); + + while ( *str ) { + if ( *str==' ' ) + ++str; + else + *str2++ = *str++; + } + *str2 = '\0'; +return( base ); +} + +static struct macname *AddMacName(FILE *ttf, + int strlen, int stroff,int spec,int language, struct macname *last) { + struct macname *new = chunkalloc(sizeof(struct macname)); + long pos = ftell(ttf); + char *pt; + int i; + + new->next = last; + new->enc = spec; + new->lang = language; + new->name = pt = galloc(strlen+1); + + fseek(ttf,stroff,SEEK_SET); + + for ( i=0; i<strlen; ++i ) + *pt++ = getc(ttf); + *pt = '\0'; + + fseek(ttf,pos,SEEK_SET); +return( new ); +} + +static void MacFeatureAdd(FILE *ttf, struct ttfinfo *info, int id, + int strlen, int stroff,int spec,int language) { + MacFeat *f; + struct macsetting *s; + + for ( f=info->features; f!=NULL; f=f->next ) { + if ( f->strid==id ) { + f->featname = AddMacName(ttf,strlen,stroff,spec,language,f->featname); +return; + } else { + for ( s=f->settings; s!=NULL; s=s->next ) { + if ( s->strid==id ) { + s->setname = AddMacName(ttf,strlen,stroff,spec,language,s->setname); +return; + } + } + } + } + /* Well, there are some things in the name table other than feature/setting*/ + /* names. Let's keep track of everything just in case.... */ + if ( info->fvar_start!=0 ) { + struct macidname *mi, *p; + for ( p=NULL, mi=info->macstrids; mi!=NULL && mi->id!=id; p = mi, mi=mi->next ); + if ( mi==NULL ) { + mi = chunkalloc(sizeof(struct macidname)); + mi->id = id; + mi->last = mi->head = AddMacName(ttf,strlen,stroff,spec,language,NULL); + if ( p==NULL ) + info->macstrids = mi; + else + p->next = mi; + } else { + mi->last->next = AddMacName(ttf,strlen,stroff,spec,language,NULL); + mi->last = mi->last->next; + } + } +} + +static void ValidatePostScriptFontName(struct ttfinfo *info, char *str) { + char *end, *pt, *npt; + int complained = false; + + /* someone gave me a font where the fontname started with the utf8 byte */ + /* order mark. PLRM says only ASCII encoding is supported. CFF says */ + /* only printable ASCII should be used */ + if ( ((uint8 *) str)[0] == 0xef && ((uint8 *) str)[1]==0xbb && ((uint8 *) str)[2] == 0xbf ) { + LogError(_("The fontname begins with the utf8 byte order sequence. This is illegal. %s"), str+3 ); + info->bad_ps_fontname = true; + for ( pt=str+3; *pt; ++pt ) + pt[-3] = *pt; /* ANSI says we can't strcpy overlapping strings */ + } + strtod(str,&end); + if ( (*end=='\0' || (isdigit(str[0]) && strchr(str,'#')!=NULL)) && + *str!='\0' ) { + ff_post_error(_("Bad Font Name"),_("A Postscript name may not be a number")); + info->bad_ps_fontname = true; + *str = 'a'; + complained = true; + } + for ( pt=str; *pt; ++pt ) { + if ( *pt<=' ' || *pt>=0x7f || + *pt=='(' || *pt=='[' || *pt=='{' || *pt=='<' || + *pt==')' || *pt==']' || *pt=='}' || *pt=='>' || + *pt=='%' || *pt=='/' ) { + if ( !complained ) { + ff_post_error(_("Bad Font Name"),_("The Postscript font name \"%.63s\" is invalid.\nIt should be printable ASCII,\nmust not contain (){}[]<>%%/ or space\nand must be shorter than 63 characters"),str); + info->bad_ps_fontname = true; + } + complained = true; + for ( npt=pt; npt[1]; ++npt ) + *npt = npt[1]; + *npt = '\0'; + --pt; + } + } + if ( strlen(str)>63 ) { + ff_post_error(_("Bad Font Name"),_("The Postscript font name \"%.63s\" is invalid.\nIt should be printable ASCII,\nmust not contain (){}[]<>%%/ or space\nand must be shorter than 63 characters"),str); + info->bad_ps_fontname = true; + str[63] = '\0'; + } +} + +char *EnforcePostScriptName(char *old) { + char *end, *pt, *npt, *str = copy(old); + + if ( old==NULL ) +return( old ); + + strtod(str,&end); + if ( (*end=='\0' || (isdigit(str[0]) && strchr(str,'#')!=NULL)) && + *str!='\0' ) { + free(str); + str=galloc(strlen(old)+2); + *str = 'a'; + strcpy(str+1,old); + } + for ( pt=str; *pt; ++pt ) { + if ( *pt<=' ' || *pt>=0x7f || + *pt=='(' || *pt=='[' || *pt=='{' || *pt=='<' || + *pt==')' || *pt==']' || *pt=='}' || *pt=='>' || + *pt=='%' || *pt=='/' ) { + for ( npt=pt; npt[1]; ++npt ) + *npt = npt[1]; + *npt = '\0'; + } + } + if ( strlen(str)>63 ) + str[63] = '\0'; +return( str ); +} + +static int IsSubSetOf(const char *substr,const char *fullstr ) { + /* The mac string is often a subset of the unicode string. Certain */ + /* characters can't be expressed in the mac encoding and are omitted */ + /* or turned to question marks or some such */ + const char *pt1, *pt2; + uint32 ch1, ch2; + + for ( pt1=substr, pt2=fullstr, ch1=utf8_ildb(&pt1); ch1!=0 ; ) { + if ( *pt2=='\0' ) + break; + ch2 = utf8_ildb(&pt2); + if ( ch1==ch2 ) + ch1 = utf8_ildb(&pt1); + } + if ( ch1=='\0' ) +return( true ); + + for ( pt1=substr, pt2=fullstr, ch1=utf8_ildb(&pt1); ch1!=0 ; ) { + if ( *pt2=='\0' ) + break; + ch2 = utf8_ildb(&pt2); + if ( ch1==ch2 || ch1=='?' ) + ch1 = utf8_ildb(&pt1); + } +return( ch1=='\0' ); +} + +static void TTFAddLangStr(FILE *ttf, struct ttfinfo *info, int id, + int strlen, int stroff,int plat,int spec,int language) { + struct ttflangname *cur, *prev; + char *str; + + if ( plat==1 && id>=256 && (info->features!=NULL || info->fvar_start!=0)) { + MacFeatureAdd(ttf,info,id,strlen,stroff,spec,language); +return; + } else if ( id<0 || id>=ttf_namemax ) +return; + + str = _readencstring(ttf,stroff,strlen,plat,spec,language); + if ( str==NULL ) /* we didn't understand the encoding */ +return; + if ( id==ttf_postscriptname ) + ValidatePostScriptFontName(info,str); + if ( *str=='\0' ) { + free(str); +return; + } + + if ( plat==1 || plat==0 ) + language = WinLangFromMac(language); + if ( (language&0xff00)==0 ) language |= 0x400; + + for ( prev=NULL, cur=info->names; cur!=NULL && cur->lang!=language; prev = cur, cur=cur->next ); + if ( cur==NULL ) { + cur = chunkalloc(sizeof(struct ttflangname)); + cur->lang = language; + if ( prev==NULL ) + info->names = cur; + else + prev->next = cur; + } + if ( cur->names[id]==NULL ) { + cur->names[id] = str; + if ( plat==1 || plat==0 ) + cur->frommac[id/32] |= (1<<(id&0x1f)); +/* There's some wacky bug in gcc. If the optimizer is on then strcmp gets turned */ +/* into some inline call. Which would be fine, except the following line goes */ +/* bluey. "Called object is not a function", etc. Compiles fine if we turn off */ +/* the inlining magic */ +#ifdef strcmp +# undef strcmp +#endif + } else if ( strcmp(str,cur->names[id])==0 ) { + free(str); + if ( plat==3 ) + cur->frommac[id/32] &= ~(1<<(id&0x1f)); + } else if ( plat==1 ) { + /* Mac string doesn't match mac unicode string */ + if ( !IsSubSetOf(str,cur->names[id]) ) + LogError( _("Warning: Mac and Unicode entries in the 'name' table differ for the\n %s string in the language %s\n Mac String: %s\nMac Unicode String: %s\n"), + TTFNameIds(id),MSLangString(language), + str,cur->names[id]); + else + LogError( _("Warning: Mac string is a subset of the Unicode string in the 'name' table\n for the %s string in the %s language.\n"), + TTFNameIds(id),MSLangString(language)); + free(str); + } else if ( plat==3 && (cur->frommac[id/32] & (1<<(id&0x1f))) ) { + if ( !IsSubSetOf(cur->names[id],str) ) + LogError( _("Warning: Mac and Windows entries in the 'name' table differ for the\n %s string in the language %s\n Mac String: %s\nWindows String: %s\n"), + TTFNameIds(id),MSLangString(language), + cur->names[id],str); + else + LogError( _("Warning: Mac string is a subset of the Windows string in the 'name' table\n for the %s string in the %s language.\n"), + TTFNameIds(id),MSLangString(language)); + free(cur->names[id]); + cur->names[id] = str; + cur->frommac[id/32] &= ~(1<<(id&0x1f)); + } else { + int ret; + if ( info->dupnamestate!=0 ) + ret = info->dupnamestate; + else + ret = 3; + if ( ret==0 || ret==1 ) + free(str); + else { + free(cur->names[id]); + cur->names[id] = str; + } + } +} + +static int is_ascii(char *str) { /* isascii is in ctype */ + if ( str==NULL ) +return( false ); + while ( *str && *str<127 && *str>=' ' ) + ++str; +return( *str=='\0' ); +} + +static char *FindLangEntry(struct ttfinfo *info, int id ) { + /* Look for an entry with string id */ + /* we prefer english, if we can't find english look for something in ascii */ + struct ttflangname *cur; + char *ret; + + for ( cur=info->names; cur!=NULL && cur->lang!=0x409; cur=cur->next ); + if ( cur!=NULL && cur->names[id]==NULL ) cur = NULL; + if ( cur==NULL ) + for ( cur=info->names; cur!=NULL && (cur->lang&0xf)!=0x09; cur=cur->next ); + if ( cur!=NULL && cur->names[id]==NULL ) cur = NULL; + if ( cur==NULL ) + for ( cur=info->names; cur!=NULL && !is_ascii(cur->names[id]); cur=cur->next ); + if ( cur==NULL ) + for ( cur=info->names; cur!=NULL && cur->names[id]==NULL; cur=cur->next ); + if ( cur==NULL ) +return( NULL ); + ret = copy(cur->names[id]); +return( ret ); +} + +struct otfname *FindAllLangEntries(FILE *ttf, struct ttfinfo *info, int id ) { + /* Look for all entries with string id under windows platform */ + int32 here = ftell(ttf); + int i, cnt, tableoff; + int platform, specific, language, name, str_len, stroff; + struct otfname *head=NULL, *cur; + + if ( info->copyright_start!=0 && id!=0 ) { + fseek(ttf,info->copyright_start,SEEK_SET); + /* format selector = */ getushort(ttf); + cnt = getushort(ttf); + tableoff = info->copyright_start+getushort(ttf); + for ( i=0; i<cnt; ++i ) { + platform = getushort(ttf); + specific = getushort(ttf); + language = getushort(ttf); + name = getushort(ttf); + str_len = getushort(ttf); + stroff = getushort(ttf); + + if ( platform==3 && name==id ) { + char *temp = _readencstring(ttf,tableoff+stroff,str_len,platform,specific,language); + if ( temp!=NULL ) { + cur = chunkalloc(sizeof(struct otfname)); + cur->next = head; + head = cur; + cur->lang = language; + cur->name = temp; + } + } + } + fseek(ttf,here,SEEK_SET); + } +return( head ); +} + +static struct macname *reversemacnames(struct macname *mn) { + struct macname *next, *prev=NULL; + + if ( mn==NULL ) +return( NULL ); + + next = mn->next; + while ( next!=NULL ) { + mn->next = prev; + prev = mn; + mn = next; + next = mn->next; + } + mn->next = prev; +return( mn ); +} + +static void readttfcopyrights(FILE *ttf,struct ttfinfo *info) { + int i, cnt, tableoff; + int platform, specific, language, name, str_len, stroff; + + if ( info->feat_start!=0 ) + readmacfeaturemap(ttf,info); + if ( info->copyright_start!=0 ) { + fseek(ttf,info->copyright_start,SEEK_SET); + /* format selector = */ getushort(ttf); + cnt = getushort(ttf); + tableoff = info->copyright_start+getushort(ttf); + for ( i=0; i<cnt; ++i ) { + platform = getushort(ttf); + specific = getushort(ttf); + language = getushort(ttf); + name = getushort(ttf); + str_len = getushort(ttf); + stroff = getushort(ttf); + + TTFAddLangStr(ttf,info,name,str_len,tableoff+stroff, + platform,specific,language); + } + } + + if ( info->copyright==NULL ) + info->copyright = FindLangEntry(info,ttf_copyright); + if ( info->familyname==NULL ) + info->familyname = FindLangEntry(info,ttf_family); + if ( info->fullname==NULL ) + info->fullname = FindLangEntry(info,ttf_fullname); + if ( info->version==NULL ) + info->version = FindLangEntry(info,ttf_version); + if ( info->fontname==NULL ) + info->fontname = FindLangEntry(info,ttf_postscriptname); + + if ( info->fontname != NULL && *info->fontname=='\0' ) { + free(info->fontname); + info->fontname = NULL; + } + if ( info->familyname != NULL && *info->familyname=='\0' ) { + free(info->familyname); + info->familyname = NULL; + } + if ( info->fullname != NULL && *info->fullname=='\0' ) { + free(info->fullname); + info->fullname = NULL; + } + + /* OpenType spec says the version string should begin with "Version " and */ + /* end with a space and have a number in between */ + if ( info->version==NULL ) info->version = copy("1.0"); + else if ( strnmatch(info->version,"Version ",8)==0 ) { + char *temp = copy(info->version+8); + if ( temp[strlen(temp)-1]==' ' ) + temp[strlen(temp)-1] = '\0'; + free(info->version); + info->version = temp; + } + if ( info->fontname==NULL ) { + if ( info->fullname!=NULL ) + info->fontname = stripspaces(copy(info->fullname)); + if ( info->fontname==NULL && info->familyname!=NULL ) + info->fontname = stripspaces(copy(info->familyname)); + if ( info->fontname!=NULL ) + ValidatePostScriptFontName(info,info->fontname); + } + + if ( info->features ) { + MacFeat *mf; + struct macsetting *ms; + for ( mf=info->features; mf!=NULL; mf = mf->next ) { + mf->featname = reversemacnames(mf->featname); + for ( ms=mf->settings; ms!=NULL; ms=ms->next ) + ms->setname = reversemacnames(ms->setname); + } + } +} + +static void readttfpreglyph(FILE *ttf,struct ttfinfo *info) { + if ( info->head_start!=0 ) + readttfhead(ttf,info); + if ( info->hhea_start!=0 ) + readttfhhea(ttf,info); + if ( info->maxp_start!=0 ) + readttfmaxp(ttf,info); + readttfcopyrights(ttf,info); /* This one has internal checks */ +} + +#define _On_Curve 1 +#define _X_Short 2 +#define _Y_Short 4 +#define _Repeat 8 +#define _X_Same 0x10 +#define _Y_Same 0x20 + +static void FigureControls(SplinePoint *from, SplinePoint *to, BasePoint *cp, + int is_order2) { + /* What are the control points for 2 cp bezier which will provide the same*/ + /* curve as that for the 1 cp bezier specified above */ + real b, c, d; + + if ( is_order2 ) { + from->nextcp = to->prevcp = *cp; + if ( cp->x==to->me.x && cp->y==to->me.y ) { + /* I would lose track of the proper location of this cp if I left */ + /* it here (would end up with from->nonextcp, which would mean I'd*/ + /* use from->me rather than to->me in tottf.c:SSAddPoints. So we */ + /* distort it a little */ + BasePoint off; + double len; + off.x = from->me.x-to->me.x; off.y = from->me.y-to->me.y; + len = sqrt(off.x*off.x+off.y*off.y); + if ( len>3 ) { + /* move the cp slightly toward from, but on the line between the two */ + from->nextcp.x = (to->prevcp.x += rint(off.x/len)); + from->nextcp.y = (to->prevcp.y += rint(off.y/len)); + } + } + } else { + d = from->me.x; + c = 2*cp->x - 2*from->me.x; + b = to->me.x+from->me.x-2*cp->x; + from->nextcp.x = d+c/3; + to->prevcp.x = from->nextcp.x + (c+b)/3; + + d = from->me.y; + c = 2*cp->y - 2*from->me.y; + b = to->me.y+from->me.y-2*cp->y; + from->nextcp.y = d+c/3; + to->prevcp.y = from->nextcp.y + (c+b)/3; + } + + if ( from->me.x!=from->nextcp.x || from->me.y!=from->nextcp.y ) + from->nonextcp = false; + if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y ) + to->noprevcp = false; + if ( is_order2 && (to->noprevcp || from->nonextcp)) { + to->noprevcp = from->nonextcp = true; + from->nextcp = from->me; + to->prevcp = to->me; + } +} + +static SplineSet *ttfbuildcontours(int path_cnt,uint16 *endpt, char *flags, + BasePoint *pts, int is_order2) { + SplineSet *head=NULL, *last=NULL, *cur; + int i, path, start, last_off; + SplinePoint *sp; + + for ( path=i=0; path<path_cnt; ++path ) { + if ( endpt[path]<i ) /* Sigh. Yes there are fonts with bad endpt info */ + continue; + cur = chunkalloc(sizeof(SplineSet)); + if ( head==NULL ) + head = cur; + else + last->next = cur; + last = cur; + last_off = false; + start = i; + sp = NULL; + while ( i<=endpt[path] ) { + if ( flags[i]&_On_Curve ) { + sp = chunkalloc(sizeof(SplinePoint)); + sp->me = sp->nextcp = sp->prevcp = pts[i]; + sp->nonextcp = sp->noprevcp = true; + sp->ttfindex = i; + sp->nextcpindex = 0xffff; + if ( last_off && cur->last!=NULL ) + FigureControls(cur->last,sp,&pts[i-1],is_order2); + last_off = false; + } else if ( last_off ) { + /* two off curve points get a third on curve point created */ + /* half-way between them. Now isn't that special */ + sp = chunkalloc(sizeof(SplinePoint)); + sp->me.x = (pts[i].x+pts[i-1].x)/2; + sp->me.y = (pts[i].y+pts[i-1].y)/2; + sp->nextcp = sp->prevcp = sp->me; + sp->nonextcp = true; + sp->ttfindex = 0xffff; + sp->nextcpindex = i; + if ( last_off && cur->last!=NULL ) + FigureControls(cur->last,sp,&pts[i-1],is_order2); + /* last_off continues to be true */ + } else { + if ( cur->first!=NULL ) + cur->last->nextcpindex = i; + last_off = true; + sp = NULL; + } + if ( sp!=NULL ) { + if ( cur->first==NULL ) + cur->first = sp; + else + SplineMake(cur->last,sp,is_order2); + cur->last = sp; + } + ++i; + } + if ( start==i-1 ) { + /* MS chinese fonts have contours consisting of a single off curve*/ + /* point. What on earth do they think that means? */ + /* Oh. I see. It's used to possition marks and such */ + if ( cur->first==NULL ) { + sp = chunkalloc(sizeof(SplinePoint)); + sp->me.x = pts[start].x; + sp->me.y = pts[start].y; + sp->nextcp = sp->prevcp = sp->me; + sp->nonextcp = sp->noprevcp = true; + sp->ttfindex = i-1; + sp->nextcpindex = 0xffff; + cur->first = cur->last = sp; + } + } else if ( !(flags[start]&_On_Curve) && !(flags[i-1]&_On_Curve) ) { + sp = chunkalloc(sizeof(SplinePoint)); + sp->me.x = (pts[start].x+pts[i-1].x)/2; + sp->me.y = (pts[start].y+pts[i-1].y)/2; + sp->nextcp = sp->prevcp = sp->me; + sp->nonextcp = true; + sp->ttfindex = 0xffff; + sp->nextcpindex = start; + FigureControls(cur->last,sp,&pts[i-1],is_order2); + SplineMake(cur->last,sp,is_order2); + cur->last = sp; + FigureControls(sp,cur->first,&pts[start],is_order2); + } else if ( !(flags[i-1]&_On_Curve)) { + FigureControls(cur->last,cur->first,&pts[i-1],is_order2); + cur->last->nextcpindex = i-1; + } else if ( !(flags[start]&_On_Curve) ) { + FigureControls(cur->last,cur->first,&pts[start],is_order2); + sp->nextcpindex = start; + } + if ( cur->last!=cur->first ) { + SplineMake(cur->last,cur->first,is_order2); + cur->last = cur->first; + } + for ( sp=cur->first; ; ) { + /*if ( sp->ttfindex!=0xffff && SPInterpolate(sp) ) + sp->dontinterpolate = true;*/ /* TODO: needs checking */ + if ( sp->next==NULL ) + break; + sp=sp->next->to; + if ( sp==cur->first ) + break; + } + } +return( head ); +} + +static void readttfsimpleglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int path_cnt) { + uint16 *endpt = galloc((path_cnt+1)*sizeof(uint16)); + uint8 *instructions; + char *flags; + BasePoint *pts; + int i, j, tot, len; + int last_pos; + + for ( i=0; i<path_cnt; ++i ) { + endpt[i] = getushort(ttf); + if ( i!=0 && endpt[i]<endpt[i-1] ) { + info->bad_glyph_data = true; + LogError( _("Bad tt font: contour ends make no sense in glyph %d.\n"), + sc->orig_pos ); +return; + } + } + if ( path_cnt==0 ) { + tot = 0; + pts = galloc(sizeof(BasePoint)); + } else { + tot = endpt[path_cnt-1]+1; + pts = galloc(tot*sizeof(BasePoint)); + } + + len = getushort(ttf); + instructions = galloc(len); + for ( i=0; i<len; ++i ) + instructions[i] = getc(ttf); + + flags = galloc(tot); + for ( i=0; i<tot; ++i ) { + flags[i] = getc(ttf); + if ( flags[i]&_Repeat ) { + int cnt = getc(ttf); + if ( i+cnt>=tot ) { + IError("Flag count is wrong (or total is): %d %d", i+cnt, tot ); + cnt = tot-i-1; + } + for ( j=0; j<cnt; ++j ) + flags[i+j+1] = flags[i]; + i += cnt; + } + if ( feof(ttf)) + break; + } + if ( i!=tot ) + IError("Flag count is wrong (or total is): %d %d in glyph %d", i, tot, sc->orig_pos ); + + last_pos = 0; + for ( i=0; i<tot; ++i ) { + if ( flags[i]&_X_Short ) { + int off = getc(ttf); + if ( !(flags[i]&_X_Same ) ) + off = -off; + pts[i].x = last_pos + off; + } else if ( flags[i]&_X_Same ) + pts[i].x = last_pos; + else + pts[i].x = last_pos + (short) getushort(ttf); + last_pos = pts[i].x; + } + + last_pos = 0; + for ( i=0; i<tot; ++i ) { + if ( flags[i]&_Y_Short ) { + int off = getc(ttf); + if ( !(flags[i]&_Y_Same ) ) + off = -off; + pts[i].y = last_pos + off; + } else if ( flags[i]&_Y_Same ) + pts[i].y = last_pos; + else + pts[i].y = last_pos + (short) getushort(ttf); + last_pos = pts[i].y; + } + + sc->layers[ly_fore].splines = ttfbuildcontours(path_cnt,endpt,flags,pts,info->to_order2); + if ( info->to_order2 && len!=0 ) { + sc->ttf_instrs_len = len; + sc->ttf_instrs = instructions; + } else + free(instructions); + SCCatagorizePoints(sc); + free(endpt); + free(flags); + free(pts); + if ( feof(ttf)) { + LogError( _("Reached end of file when reading simple glyph\n") ); + info->bad_glyph_data = true; + } +} + +static void readttfcompositglyph(FILE *ttf,struct ttfinfo *info,SplineChar *sc, int32 end) { + RefChar *head=NULL, *last=NULL, *cur=NULL; + int flags=0, arg1, arg2; + int use_my_metrics=0; + + if ( ftell(ttf)>=end ) { + LogError( _("Empty composite %d\n"), sc->orig_pos ); + info->bad_glyph_data = true; +return; + } + + do { + if ( ftell(ttf)>=end ) { + LogError( _("Bad flags value, implied MORE components at end of glyph %d\n"), sc->orig_pos ); + info->bad_glyph_data = true; + break; + } + cur = RefCharCreate(); + flags = getushort(ttf); + cur->orig_pos = getushort(ttf); + if ( feof(ttf) || cur->orig_pos>=info->glyph_cnt ) { + LogError(_("Reference to glyph %d out of bounds when parsing 'glyf' table.\n"), cur->orig_pos ); + info->bad_glyph_data = true; + cur->orig_pos = 0; + } + if ( info->inuse!=NULL ) + info->inuse[cur->orig_pos] = true; + if ( flags&_ARGS_ARE_WORDS ) { + arg1 = (short) getushort(ttf); + arg2 = (short) getushort(ttf); + } else { + arg1 = (signed char) getc(ttf); + arg2 = (signed char) getc(ttf); + } + cur->use_my_metrics = (flags & _USE_MY_METRICS) ? 1 : 0; + if ( cur->use_my_metrics ) { + if ( use_my_metrics ) { + LogError( _("Use-my-metrics flag set on at least two components in glyph %d\n"), sc->orig_pos ); + info->bad_glyph_data = true; + } else + use_my_metrics = true; + } + cur->round_translation_to_grid = (flags & _ROUND) ? 1 : 0; + if ( flags & _ARGS_ARE_XY ) { + /* There is some very strange stuff (half-)documented on the apple*/ + /* site about how these should be interpretted when there are */ + /* scale factors, or rotations */ + /* It isn't well enough described to be comprehensible */ + /* http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html */ + /* Microsoft says nothing about this */ + /* Adobe implies this is a difference between MS and Apple */ + /* MS doesn't do this, Apple does (GRRRGH!!!!) */ + /* Adobe says that setting bit 12 means that this will not happen */ + /* Adobe says that setting bit 11 means that this will happen */ + /* So if either bit is set we know when this happens, if neither */ + /* we guess... But I still don't know how to interpret the */ + /* apple mode under rotation... */ + /* I notice that FreeType does nothing about rotation nor does it */ + /* interpret bits 11&12 */ + /* Ah. It turns out that even Apple does not do what Apple's docs */ + /* claim it does. I think I've worked it out (see below), but... */ + /* Bleah! */ + cur->transform[4] = arg1; + cur->transform[5] = arg2; + } else { + /* Somehow we can get offsets by looking at the points in the */ + /* points so far generated and comparing them to the points in */ + /* the current componant */ + /* How exactly is not described on any of the Apple, MS, Adobe */ + /* freetype looks up arg1 in the set of points we've got so far */ + /* looks up arg2 in the new component (before renumbering) */ + /* offset.x = arg1.x - arg2.x; offset.y = arg1.y - arg2.y; */ + /* This fixup needs to be done later though (after all glyphs */ + /* have been loaded) */ + cur->match_pt_base = arg1; + cur->match_pt_ref = arg2; + cur->point_match = true; + } + cur->transform[0] = cur->transform[3] = 1.0; + if ( flags & _SCALE ) + cur->transform[0] = cur->transform[3] = get2dot14(ttf); + else if ( flags & _XY_SCALE ) { + cur->transform[0] = get2dot14(ttf); + cur->transform[3] = get2dot14(ttf); + } else if ( flags & _MATRIX ) { + cur->transform[0] = get2dot14(ttf); + cur->transform[1] = get2dot14(ttf); + cur->transform[2] = get2dot14(ttf); + cur->transform[3] = get2dot14(ttf); + } + if ( flags & _ARGS_ARE_XY ) { /* Only muck with these guys if they are real offsets and not point matching */ +#ifdef __Mac + /* On mac assume scaled offsets unless told unscaled explicitly */ + if ( !(flags&_UNSCALED_OFFSETS) && +#else + /* everywhere else assume unscaled offsets unless told scaled explicitly */ + if ( (flags & _SCALED_OFFSETS) && +#endif + (flags & _ARGS_ARE_XY) && (flags&(_SCALE|_XY_SCALE|_MATRIX))) { + /*static int asked = 0;*/ + /* This is not what Apple documents on their website. But it is */ + /* what appears to match the behavior of their rasterizer */ + /* Apple has changed their documentation (without updating their */ + /* changelog), but I believe they are still incorrect */ + cur->transform[4] *= sqrt(cur->transform[0]*cur->transform[0]+ + cur->transform[1]*cur->transform[1]); + cur->transform[5] *= sqrt(cur->transform[2]*cur->transform[2]+ + cur->transform[3]*cur->transform[3]); +#if 0 + /* Apple's Chicago is an example */ + if ( info->fontname!=NULL && strcmp(info->fontname,"CompositeMac")!=0 && !asked ) { + /* Not interested in the test font I generated myself */ + asked = true; + fprintf( stderr, "Neat! You've got a font that actually uses Apple's scaled composite offsets.\n" ); + fprintf( stderr, " I've never seen one, could you send me a copy of %s?\n", info->fontname ); + fprintf( stderr, " Thanks. gww@silcom.com\n" ); + } +#endif + } + } + if ( cur->orig_pos>=info->glyph_cnt ) { + LogError(_("Glyph %d attempts to reference glyph %d which is outside the font\n"), sc->orig_pos, cur->orig_pos ); + chunkfree(cur,sizeof(*cur)); + } else { + if ( head==NULL ) + head = cur; + else + last->next = cur; + last = cur; + } + if ( feof(ttf)) { + LogError(_("Reached end of file when reading composit glyph\n") ); + info->bad_glyph_data = true; + break; + } + } while ( flags&_MORE ); + if ( (flags & _INSTR ) && info->to_order2 && ftell(ttf)<end ) { + sc->ttf_instrs_len = getushort(ttf); + if ( sc->ttf_instrs_len > 0 && ftell(ttf)+sc->ttf_instrs_len<=end ) { + uint8 *instructions = galloc(sc->ttf_instrs_len); + int i; + for ( i=0; i<sc->ttf_instrs_len; ++i ) + instructions[i] = getc(ttf); + sc->ttf_instrs = instructions; + } else + sc->ttf_instrs_len = 0; + } + sc->layers[ly_fore].refs = head; +} + +static SplineChar *readttfglyph(FILE *ttf,struct ttfinfo *info,int start, int end,int gid) { + int path_cnt; + SplineChar *sc = SplineCharCreate(2); + + sc->unicodeenc = -1; + sc->vwidth = info->emsize; + sc->orig_pos = gid; + + if ( end>(int)info->glyph_length ) { + if ( !info->complainedbeyondglyfend ) + LogError(_("Bad glyph (%d), its definition extends beyond the end of the glyf table\n"), gid ); + info->bad_glyph_data = true; + info->complainedbeyondglyfend = true; + SplineCharFree(sc); +return( NULL ); + } else if ( end<start ) { + LogError(_("Bad glyph (%d), its data length is negative\n"), gid ); + SplineCharFree(sc); +return( NULL ); + } + + if ( start==end ) { + /* This isn't mentioned, but we seem to get some glyphs with no size,*/ + /* not even a path cnt. They appear to be empty glyphs */ +return( sc ); + } + fseek(ttf,info->glyph_start+start,SEEK_SET); + path_cnt = (short) getushort(ttf); + +#ifdef LUA_FF_LIB + sc->xmin = getushort(ttf); + sc->ymin = getushort(ttf); + sc->xmax = getushort(ttf); + sc->ymax = getushort(ttf); + sc->lsidebearing = sc->xmin; +#else + /* xmin = */ sc->lsidebearing = getushort(ttf); + /* ymin = */ getushort(ttf); + /* xmax = */ getushort(ttf); + /* ymax = */ /* sc->lsidebearing = */ getushort(ttf); /* what was this for? */ +#endif + if ( path_cnt>=0 ) + readttfsimpleglyph(ttf,info,sc,path_cnt); + else + readttfcompositglyph(ttf,info,sc,info->glyph_start+end); + if ( start>end ) { + LogError(_("Bad glyph (%d), disordered 'loca' table (start comes after end)\n"), gid ); + info->bad_glyph_data = true; + } else if ( ftell(ttf)>info->glyph_start+end ) { + LogError(_("Bad glyph (%d), its definition extends beyond the space allowed for it\n"), gid ); + info->bad_glyph_data = true; + } + + /* find the bb */ + THPatchSplineChar(sc); + +return( sc ); +} + +static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse); + +static void readttfglyphs(FILE *ttf,struct ttfinfo *info) { + int i, anyread; + uint32 *goffsets = galloc((info->glyph_cnt+1)*sizeof(uint32)); + + /* First we read all the locations. This might not be needed, they may */ + /* just follow one another, but nothing I've noticed says that so let's */ + /* be careful */ + fseek(ttf,info->glyphlocations_start,SEEK_SET); + if ( info->index_to_loc_is_long ) { + for ( i=0; i<=info->glyph_cnt ; ++i ) + goffsets[i] = getlong(ttf); + } else { + for ( i=0; i<=info->glyph_cnt ; ++i ) + goffsets[i] = 2*getushort(ttf); + } + + info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *)); + if ( !info->is_ttc || (info->openflags&of_all_glyphs_in_ttc)) { + /* read all the glyphs */ + for ( i=0; i<info->glyph_cnt ; ++i ) { + info->chars[i] = readttfglyph(ttf,info,goffsets[i],goffsets[i+1],i); + ff_progress_next(); + } + } else { + /* only read the glyphs we actually use in this font */ + /* this is complicated by references (and substitutions), */ + /* we can't just rely on the encoding to tell us what is used */ + info->inuse = gcalloc(info->glyph_cnt,sizeof(char)); + readttfencodings(ttf,info,git_justinuse); + if ( info->gsub_start!=0 ) /* Some glyphs may appear in substitutions and not in the encoding... */ + readttfgsubUsed(ttf,info); + if ( info->math_start!=0 ) + otf_read_math_used(ttf,info); + /* I don't bother to read the morx table because mac doesn't */ + /* support ttc files */ + anyread = true; + while ( anyread ) { + anyread = false; + for ( i=0; i<info->glyph_cnt ; ++i ) { + if ( info->inuse[i] && info->chars[i]==NULL ) { + info->chars[i] = readttfglyph(ttf,info,goffsets[i],goffsets[i+1],i); + ff_progress_next(); + anyread = info->chars[i]!=NULL; + } + } + } + free(info->inuse); info->inuse = NULL; + } + free(goffsets); + for ( i=0; i<info->glyph_cnt ; ++i ) + if ( info->chars[i]!=NULL ) + info->chars[i]->orig_pos = i; + ff_progress_next_stage(); +} + +/* Standard names for cff */ +const char *cffnames[] = { + ".notdef", + "space", + "exclam", + "quotedbl", + "numbersign", + "dollar", + "percent", + "ampersand", + "quoteright", + "parenleft", + "parenright", + "asterisk", + "plus", + "comma", + "hyphen", + "period", + "slash", + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "colon", + "semicolon", + "less", + "equal", + "greater", + "question", + "at", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "bracketleft", + "backslash", + "bracketright", + "asciicircum", + "underscore", + "quoteleft", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "braceleft", + "bar", + "braceright", + "asciitilde", + "exclamdown", + "cent", + "sterling", + "fraction", + "yen", + "florin", + "section", + "currency", + "quotesingle", + "quotedblleft", + "guillemotleft", + "guilsinglleft", + "guilsinglright", + "fi", + "fl", + "endash", + "dagger", + "daggerdbl", + "periodcentered", + "paragraph", + "bullet", + "quotesinglbase", + "quotedblbase", + "quotedblright", + "guillemotright", + "ellipsis", + "perthousand", + "questiondown", + "grave", + "acute", + "circumflex", + "tilde", + "macron", + "breve", + "dotaccent", + "dieresis", + "ring", + "cedilla", + "hungarumlaut", + "ogonek", + "caron", + "emdash", + "AE", + "ordfeminine", + "Lslash", + "Oslash", + "OE", + "ordmasculine", + "ae", + "dotlessi", + "lslash", + "oslash", + "oe", + "germandbls", + "onesuperior", + "logicalnot", + "mu", + "trademark", + "Eth", + "onehalf", + "plusminus", + "Thorn", + "onequarter", + "divide", + "brokenbar", + "degree", + "thorn", + "threequarters", + "twosuperior", + "registered", + "minus", + "eth", + "multiply", + "threesuperior", + "copyright", + "Aacute", + "Acircumflex", + "Adieresis", + "Agrave", + "Aring", + "Atilde", + "Ccedilla", + "Eacute", + "Ecircumflex", + "Edieresis", + "Egrave", + "Iacute", + "Icircumflex", + "Idieresis", + "Igrave", + "Ntilde", + "Oacute", + "Ocircumflex", + "Odieresis", + "Ograve", + "Otilde", + "Scaron", + "Uacute", + "Ucircumflex", + "Udieresis", + "Ugrave", + "Yacute", + "Ydieresis", + "Zcaron", + "aacute", + "acircumflex", + "adieresis", + "agrave", + "aring", + "atilde", + "ccedilla", + "eacute", + "ecircumflex", + "edieresis", + "egrave", + "iacute", + "icircumflex", + "idieresis", + "igrave", + "ntilde", + "oacute", + "ocircumflex", + "odieresis", + "ograve", + "otilde", + "scaron", + "uacute", + "ucircumflex", + "udieresis", + "ugrave", + "yacute", + "ydieresis", + "zcaron", + "exclamsmall", + "Hungarumlautsmall", + "dollaroldstyle", + "dollarsuperior", + "ampersandsmall", + "Acutesmall", + "parenleftsuperior", + "parenrightsuperior", + "twodotenleader", + "onedotenleader", + "zerooldstyle", + "oneoldstyle", + "twooldstyle", + "threeoldstyle", + "fouroldstyle", + "fiveoldstyle", + "sixoldstyle", + "sevenoldstyle", + "eightoldstyle", + "nineoldstyle", + "commasuperior", + "threequartersemdash", + "periodsuperior", + "questionsmall", + "asuperior", + "bsuperior", + "centsuperior", + "dsuperior", + "esuperior", + "isuperior", + "lsuperior", + "msuperior", + "nsuperior", + "osuperior", + "rsuperior", + "ssuperior", + "tsuperior", + "ff", + "ffi", + "ffl", + "parenleftinferior", + "parenrightinferior", + "Circumflexsmall", + "hyphensuperior", + "Gravesmall", + "Asmall", + "Bsmall", + "Csmall", + "Dsmall", + "Esmall", + "Fsmall", + "Gsmall", + "Hsmall", + "Ismall", + "Jsmall", + "Ksmall", + "Lsmall", + "Msmall", + "Nsmall", + "Osmall", + "Psmall", + "Qsmall", + "Rsmall", + "Ssmall", + "Tsmall", + "Usmall", + "Vsmall", + "Wsmall", + "Xsmall", + "Ysmall", + "Zsmall", + "colonmonetary", + "onefitted", + "rupiah", + "Tildesmall", + "exclamdownsmall", + "centoldstyle", + "Lslashsmall", + "Scaronsmall", + "Zcaronsmall", + "Dieresissmall", + "Brevesmall", + "Caronsmall", + "Dotaccentsmall", + "Macronsmall", + "figuredash", + "hypheninferior", + "Ogoneksmall", + "Ringsmall", + "Cedillasmall", + "questiondownsmall", + "oneeighth", + "threeeighths", + "fiveeighths", + "seveneighths", + "onethird", + "twothirds", + "zerosuperior", + "foursuperior", + "fivesuperior", + "sixsuperior", + "sevensuperior", + "eightsuperior", + "ninesuperior", + "zeroinferior", + "oneinferior", + "twoinferior", + "threeinferior", + "fourinferior", + "fiveinferior", + "sixinferior", + "seveninferior", + "eightinferior", + "nineinferior", + "centinferior", + "dollarinferior", + "periodinferior", + "commainferior", + "Agravesmall", + "Aacutesmall", + "Acircumflexsmall", + "Atildesmall", + "Adieresissmall", + "Aringsmall", + "AEsmall", + "Ccedillasmall", + "Egravesmall", + "Eacutesmall", + "Ecircumflexsmall", + "Edieresissmall", + "Igravesmall", + "Iacutesmall", + "Icircumflexsmall", + "Idieresissmall", + "Ethsmall", + "Ntildesmall", + "Ogravesmall", + "Oacutesmall", + "Ocircumflexsmall", + "Otildesmall", + "Odieresissmall", + "OEsmall", + "Oslashsmall", + "Ugravesmall", + "Uacutesmall", + "Ucircumflexsmall", + "Udieresissmall", + "Yacutesmall", + "Thornsmall", + "Ydieresissmall", + "001.000", + "001.001", + "001.002", + "001.003", + "Black", + "Bold", + "Book", + "Light", + "Medium", + "Regular", + "Roman", + "Semibold", + NULL +}; +const int nStdStrings = sizeof(cffnames)/sizeof(cffnames[0])-1; + +static char **readcfffontnames(FILE *ttf,int *cnt,struct ttfinfo *info) { + uint16 count = getushort(ttf); + int offsize; + uint32 *offsets; + char **names; + int i,j; + + if ( cnt!=NULL ) *cnt = count; + + if ( count==0 ) +return( NULL ); + offsets = galloc((count+1)*sizeof(uint32)); + offsize = getc(ttf); + for ( i=0; i<=count; ++i ) + offsets[i] = getoffset(ttf,offsize); + names = galloc((count+1)*sizeof(char *)); + for ( i=0; i<count; ++i ) { + if ( offsets[i+1]<offsets[i] ) { +/* GT: The CFF font type contains a thing called a name INDEX, and that INDEX */ +/* GT: is bad. It is an index of many of the names used in the CFF font. */ +/* GT: We hope the user will never see this. */ + LogError( _("Bad CFF name INDEX\n") ); + if ( info!=NULL ) info->bad_cff = true; + while ( i<count ) { + names[i] = copy(""); + ++i; + } + --i; + } else { + names[i] = galloc(offsets[i+1]-offsets[i]+1); + for ( j=0; j<(int)(offsets[i+1]-offsets[i]); ++j ) + names[i][j] = getc(ttf); + names[i][j] = '\0'; + } + } + names[i] = NULL; + free(offsets); +return( names ); +} + +static char *addnibble(char *pt, int nib) { + if ( nib<=9 ) + *pt++ = nib+'0'; + else if ( nib==10 ) + *pt++ = '.'; + else if ( nib==11 ) + *pt++ = 'E'; + else if ( nib==12 ) { + *pt++ = 'E'; + *pt++ = '-'; + } else if ( nib==14 ) + *pt++ = '-'; + else if ( nib==15 ) + *pt++ = '\0'; +return( pt ); +} + +static int readcffthing(FILE *ttf,int *_ival,real *dval,int *operand,struct ttfinfo *info) { + char buffer[50], *pt; + int ch, ival; + + ch = getc(ttf); + if ( ch==12 ) { + *operand = (12<<8) | getc(ttf); +return( 3 ); + } else if ( ch<=21 ) { + *operand = ch; +return( 3 ); + } else if ( ch==30 ) { + /* fixed format doesn't exist in dict data but does in type2 strings */ + pt = buffer; + do { + ch = getc(ttf); + if ( pt<buffer+44 || (ch&0xf)==0xf || (ch&0xf0)==0xf0 ) { + pt = addnibble(pt,ch>>4); + pt = addnibble(pt,ch&0xf); + } + } while ( pt[-1]!='\0' ); + *dval = strtod(buffer,NULL); +return( 2 ); + } else if ( ch>=32 && ch<=246 ) { + *_ival = ch-139; +return( 1 ); + } else if ( ch>=247 && ch<=250 ) { + *_ival = ((ch-247)<<8) + getc(ttf)+108; +return( 1 ); + } else if ( ch>=251 && ch<=254 ) { + *_ival = -((ch-251)<<8) - getc(ttf)-108; +return( 1 ); + } else if ( ch==28 ) { + ival = getc(ttf)<<8; + *_ival = (short) (ival | getc(ttf)); +return( 1 ); + } else if ( ch==29 ) { + /* 4 byte integers exist in dict data but not in type2 strings */ + ival = getc(ttf)<<24; + ival = ival | getc(ttf)<<16; + ival = ival | getc(ttf)<<8; + *_ival = (int) (ival | getc(ttf)); +return( 1 ); + } + LogError(_("Unexpected value in dictionary %d\n"), ch ); + info->bad_cff = true; + *_ival = 0; +return( 0 ); +} + +static void skipcfft2thing(FILE *ttf) { + /* The old CFF spec allows little type2 programs to live in the CFF dict */ + /* indices. These are designed to allow interpolation of values for mm */ + /* fonts. */ + /* The Type2 program is terminated by an "endchar" operator */ + /* I don't support this, but I shall try to skip over them properly */ + /* There's no discussion about how values move from the t2 stack to the */ + /* cff stack, as there are no examples of this, it's hard to guess */ + int ch; + +/* GT: DICT is a magic term inside CFF fonts, as is INDEX, and I guess CFF and type2 */ + LogError( _("FontForge does not support type2 programs embedded in CFF DICT INDICES.\n") ); + forever { + ch = getc(ttf); + if ( ch>=247 && ch<=254 ) + getc(ttf); /* Two byte number */ + else if ( ch==255 ) { + getc(ttf); getc(ttf); getc(ttf); getc(ttf); + /* 16.16 number */ + } else if ( ch==28 ) { + getc(ttf); + getc(ttf); + } else if ( ch==12 ) { + getc(ttf); /* Two byte operator */ + } else if ( ch==14 ) { +return; + } + } +} + +struct topdicts { + int32 cff_start; + + char *fontname; /* From Name Index */ + + int version; /* SID */ + int notice; /* SID */ + int copyright; /* SID */ + int fullname; /* SID */ + int familyname; /* SID */ + int weight; /* SID */ + int isfixedpitch; + real italicangle; + real underlinepos; + real underlinewidth; + int painttype; + int charstringtype; + real fontmatrix[6]; + int fontmatrix_set; + int uniqueid; + real fontbb[4]; + real strokewidth; + int xuid[20]; + int charsetoff; /* from start of file */ + int encodingoff; /* from start of file */ + int charstringsoff; /* from start of file */ + int private_size; + int private_offset; /* from start of file */ + int synthetic_base; /* font index */ + int postscript_code; /* SID */ + /* synthetic fonts only (whatever they are) */ + int basefontname; /* SID */ + /* Multiple master/synthetic fonts */ + real basefontblend[16]; /* delta */ /* No description of why this is relevant for mm fonts */ + /* Multiple master fonts only */ + int blendaxistypes[17]; /* SID */ + int nMasters; + int nAxes; + real weightvector[17]; + int lenBuildCharArray; /* No description of what this means */ + int NormalizeDesignVector; /* SID */ /* No description of what this does */ + int ConvertDesignVector; /* SID */ /* No description of what this does */ + /* CID fonts only */ + int ros_registry; /* SID */ + int ros_ordering; /* SID */ + int ros_supplement; + real cidfontversion; + int cidfontrevision; + int cidfonttype; + int cidcount; + int uidbase; + int fdarrayoff; /* from start of file */ + int fdselectoff; /* from start of file */ + int sid_fontname; /* SID */ +/* Private stuff */ + real bluevalues[14]; + real otherblues[10]; + real familyblues[14]; + real familyotherblues[10]; + real bluescale; + real blueshift; + real bluefuzz; + int stdhw; + int stdvw; + real stemsnaph[10]; + real stemsnapv[10]; + int forcebold; + real forceboldthreshold; + int languagegroup; + real expansionfactor; + int initialRandomSeed; + int subrsoff; /* from start of this private table */ + int defaultwidthx; + int nominalwidthx; + + struct pschars glyphs; + struct pschars local_subrs; + uint16 *charset; +}; + +static void TopDictFree(struct topdicts *dict) { + int i; + + free(dict->charset); + for ( i=0; i<dict->glyphs.cnt; ++i ) + free(dict->glyphs.values[i]); + free(dict->glyphs.values); + free(dict->glyphs.lens); + for ( i=0; i<dict->local_subrs.cnt; ++i ) + free(dict->local_subrs.values[i]); + free(dict->local_subrs.values); + free(dict->local_subrs.lens); + free(dict); +} + +static void readcffsubrs(FILE *ttf, struct pschars *subs, struct ttfinfo *info) { + uint16 count = getushort(ttf); + int offsize; + uint32 *offsets; + int i,j, base; + int err = false; + + memset(subs,'\0',sizeof(struct pschars)); + if ( count==0 ) +return; + subs->cnt = count; + subs->lens = galloc(count*sizeof(int)); + subs->values = galloc(count*sizeof(uint8 *)); + offsets = galloc((count+1)*sizeof(uint32)); + offsize = getc(ttf); + for ( i=0; i<=count; ++i ) + offsets[i] = getoffset(ttf,offsize); + base = ftell(ttf)-1; + for ( i=0; i<count; ++i ) { + if ( offsets[i+1]>offsets[i] && offsets[i+1]-offsets[i]<0x10000 ) { + subs->lens[i] = offsets[i+1]-offsets[i]; + subs->values[i] = galloc(offsets[i+1]-offsets[i]+1); + for ( j=0; j<(int)(offsets[i+1]-offsets[i]); ++j ) + subs->values[i][j] = getc(ttf); + subs->values[i][j] = '\0'; + } else { + if ( !err ) + LogError( _("Bad subroutine INDEX in cff font.\n" )); + info->bad_cff = true; + err = true; + subs->lens[i] = 1; + subs->values[i] = galloc(2); + subs->values[i][0] = 11; /* return */ + subs->values[i][1] = '\0'; + fseek(ttf,base+offsets[i+1],SEEK_SET); + } + } + free(offsets); +} + +static struct topdicts *readcfftopdict(FILE *ttf, char *fontname, int len, + struct ttfinfo *info) { + struct topdicts *td = gcalloc(1,sizeof(struct topdicts)); + long base = ftell(ttf); + int ival, oval, sp, ret, i; + real stack[50]; + + if ( fontname!=NULL ) + ValidatePostScriptFontName(info,fontname); + + td->fontname = fontname; + td->underlinepos = -100; + td->underlinewidth = 50; + td->charstringtype = 2; + td->fontmatrix[0] = td->fontmatrix[3] = .001; + + td->notice = td->copyright = td->fullname = td->familyname = td->weight = td->version = -1; + td->postscript_code = td->basefontname = -1; + td->synthetic_base = td->ros_registry = -1; + td->fdarrayoff = td->fdselectoff = td->sid_fontname = -1; + td->blendaxistypes[0] = -1; + + /* Multiple master fonts can have Type2 operators here, particularly */ + /* blend operators. We're ignoring that */ + while ( ftell(ttf)<base+len ) { + sp = 0; + while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=3 && ftell(ttf)<base+len ) { + if ( ret==1 ) + stack[sp]=ival; + if ( ret!=0 && sp<45 ) + ++sp; + } + if ( ret==3 && oval==31 /* "T2" operator, can have 0 arguments */ ) { + skipcfft2thing(ttf); + } else if ( sp==0 ) { + LogError( _("No argument to operator\n") ); + info->bad_cff = true; + } else if ( ret==3 ) switch( oval ) { + case 0: + td->version = stack[sp-1]; + break; + case 1: + td->notice = stack[sp-1]; + break; + case (12<<8)+0: + td->copyright = stack[sp-1]; + break; + case 2: + td->fullname = stack[sp-1]; + break; + case 3: + td->familyname = stack[sp-1]; + break; + case 4: + td->weight = stack[sp-1]; + break; + case (12<<8)+1: + td->isfixedpitch = stack[sp-1]; + break; + case (12<<8)+2: + td->italicangle = stack[sp-1]; + break; + case (12<<8)+3: + td->underlinepos = stack[sp-1]; + break; + case (12<<8)+4: + td->underlinewidth = stack[sp-1]; + break; + case (12<<8)+5: + td->painttype = stack[sp-1]; + break; + case (12<<8)+6: + td->charstringtype = stack[sp-1]; + break; + case (12<<8)+7: + memcpy(td->fontmatrix,stack,(sp>=6?6:sp)*sizeof(real)); + td->fontmatrix_set = 1; + break; + case 13: + td->uniqueid = stack[sp-1]; + break; + case 5: + memcpy(td->fontbb,stack,(sp>=4?4:sp)*sizeof(real)); + break; + case (12<<8)+8: + td->strokewidth = stack[sp-1]; + break; + case 14: + for ( i=0; i<sp && i<20; ++i ) + td->xuid[i] = stack[i]; + break; + case 15: + td->charsetoff = stack[sp-1]; + break; + case 16: + td->encodingoff = stack[sp-1]; + break; + case 17: + td->charstringsoff = stack[sp-1]; + break; + case 18: + td->private_size = stack[0]; + td->private_offset = stack[1]; + break; + case (12<<8)+20: + LogError( _("FontForge does not support synthetic fonts\n") ); + td->synthetic_base = stack[sp-1]; + break; + case (12<<8)+21: + td->postscript_code = stack[sp-1]; + break; + case (12<<8)+22: + td->basefontname = stack[sp-1]; + break; + case (12<<8)+23: + for ( i=0; i<sp && i<16; ++i ) + td->basefontblend[i] = stack[i]; + break; + case (12<<8)+24: + LogError( _("FontForge does not support type2 multiple master fonts\n") ); + info->bad_cff = true; + td->nMasters = stack[0]; + td->nAxes = sp-4; + memcpy(td->weightvector,stack+1,(sp-4)*sizeof(real)); + td->lenBuildCharArray = stack[sp-3]; + td->NormalizeDesignVector = stack[sp-2]; /* These are type2 charstrings, even in type1 fonts */ + td->ConvertDesignVector = stack[sp-1]; + break; + case (12<<8)+26: + for ( i=0; i<sp && i<16; ++i ) + td->blendaxistypes[i] = stack[i]; + td->blendaxistypes[i] = -1; + break; + case (12<<8)+30: + td->ros_registry = stack[0]; + td->ros_ordering = stack[1]; + td->ros_supplement = stack[2]; + break; + case (12<<8)+31: + td->cidfontversion = stack[sp-1]; + break; + case (12<<8)+32: + td->cidfontrevision = stack[sp-1]; + break; + case (12<<8)+33: + td->cidfonttype = stack[sp-1]; + break; + case (12<<8)+34: + td->cidcount = stack[sp-1]; + break; + case (12<<8)+35: + td->uidbase = stack[sp-1]; + break; + case (12<<8)+36: + td->fdarrayoff = stack[sp-1]; + break; + case (12<<8)+37: + td->fdselectoff = stack[sp-1]; + break; + case (12<<8)+38: + td->sid_fontname = stack[sp-1]; + break; + case (12<<8)+39: + LogError(_("FontForge does not support Chameleon fonts\n"));; + break; + default: + LogError(_("Unknown operator in %s: %x\n"), fontname, oval ); + info->bad_cff = true; + break; + } + } +return( td ); +} + +static void readcffprivate(FILE *ttf, struct topdicts *td, struct ttfinfo *info) { + int ival, oval, sp, ret, i; + real stack[50]; + int32 end = td->cff_start+td->private_offset+td->private_size; + + fseek(ttf,td->cff_start+td->private_offset,SEEK_SET); + + td->subrsoff = -1; + td->expansionfactor = .06; + td->bluefuzz = 1; + td->blueshift = 7; + td->bluescale = .039625; + + while ( ftell(ttf)<end ) { + if ( feof(ttf) ) { + LogError("End of file found when reading private dictionary.\n" ); + break; + } + sp = 0; + while ( (ret=readcffthing(ttf,&ival,&stack[sp],&oval,info))!=3 && ftell(ttf)<end ) { + if ( ret==1 ) + stack[sp]=ival; + if ( ret!=0 && sp<45 ) + ++sp; + } + if ( ret==3 && oval==31 /* "T2" operator, can have 0 arguments */ ) { + skipcfft2thing(ttf); + } else if ( sp==0 && oval!=6 && oval!=7 && oval!=8 && oval!=9 && oval !=(12<<8)+12 && oval !=(12<<8)+13) { + LogError( _("No argument to operator %d in private dict\n"), oval ); + info->bad_cff = true; + } else if ( ret==3 ) switch( oval ) { + case 6: + for ( i=0; i<sp && i<14; ++i ) { + td->bluevalues[i] = stack[i]; + if ( i!=0 ) + td->bluevalues[i] += td->bluevalues[i-1]; + } + break; + case 7: + for ( i=0; i<sp && i<10; ++i ) { + td->otherblues[i] = stack[i]; + if ( i!=0 ) + td->otherblues[i] += td->otherblues[i-1]; + } + break; + case 8: + for ( i=0; i<sp && i<14; ++i ) { + td->familyblues[i] = stack[i]; + if ( i!=0 ) + td->familyblues[i] += td->familyblues[i-1]; + } + break; + case 9: + for ( i=0; i<sp && i<10; ++i ) { + td->familyotherblues[i] = stack[i]; + if ( i!=0 ) + td->familyotherblues[i] += td->familyotherblues[i-1]; + } + break; + case (12<<8)+9: + td->bluescale = stack[sp-1]; + break; + case (12<<8)+10: + td->blueshift = stack[sp-1]; + break; + case (12<<8)+11: + td->bluefuzz = stack[sp-1]; + break; + case 10: + td->stdhw = stack[sp-1]; + break; + case 11: + td->stdvw = stack[sp-1]; + break; + case (12<<8)+12: + for ( i=0; i<sp && i<10; ++i ) { + td->stemsnaph[i] = stack[i]; + if ( i!=0 ) + td->stemsnaph[i] += td->stemsnaph[i-1]; + } + break; + case (12<<8)+13: + for ( i=0; i<sp && i<10; ++i ) { + td->stemsnapv[i] = stack[i]; + if ( i!=0 ) + td->stemsnapv[i] += td->stemsnapv[i-1]; + } + break; + case (12<<8)+14: + td->forcebold = stack[sp-1]; + break; + case (12<<8)+15: /* obsolete */ + td->forceboldthreshold = stack[sp-1]; + break; + case (12<<8)+16: + /* lenIV. -1 => unencrypted charstrings */ + /* obsolete */ + break; + case (12<<8)+17: + td->languagegroup = stack[sp-1]; + break; + case (12<<8)+18: + td->expansionfactor = stack[sp-1]; + break; + case (12<<8)+19: + td->initialRandomSeed = stack[sp-1]; + break; + case 19: + td->subrsoff = stack[sp-1]; + break; + case 20: + td->defaultwidthx = stack[sp-1]; + break; + case 21: + td->nominalwidthx = stack[sp-1]; + break; + default: + LogError(_("Unknown operator in %s: %x\n"), td->fontname, oval ); + info->bad_cff = true; + break; + } + } + + if ( td->subrsoff!=-1 ) { + fseek(ttf,td->cff_start+td->private_offset+td->subrsoff,SEEK_SET); + readcffsubrs(ttf,&td->local_subrs,info); + } +} + +static struct topdicts **readcfftopdicts(FILE *ttf, char **fontnames, int32 cff_start, + struct ttfinfo *info, struct topdicts *parent_dict) { + uint16 count = getushort(ttf); + int offsize; + uint32 *offsets; + struct topdicts **dicts; + int i; + + if ( count==0 ) +return( NULL ); + offsets = galloc((count+1)*sizeof(uint32)); + offsize = getc(ttf); + for ( i=0; i<=count; ++i ) + offsets[i] = getoffset(ttf,offsize); + dicts = galloc((count+1)*sizeof(struct topdicts *)); + for ( i=0; i<count; ++i ) { + dicts[i] = readcfftopdict(ttf,fontnames!=NULL?fontnames[i]:NULL, + offsets[i+1]-offsets[i], info); + if ( parent_dict!=NULL && parent_dict->fontmatrix_set ) { + MatMultiply(parent_dict->fontmatrix,dicts[i]->fontmatrix,dicts[i]->fontmatrix); + } + dicts[i]->cff_start = cff_start; + } + dicts[i] = NULL; + free(offsets); +return( dicts ); +} + +static const char *getsid(int sid,char **strings,int scnt,struct ttfinfo *info) { + if ( sid==-1 ) +return( NULL ); + else if ( sid<nStdStrings ) +return( cffnames[sid] ); + else if ( sid-nStdStrings>scnt ) { + LogError( _("Bad sid %d (must be less than %d)\n"), sid, scnt+nStdStrings ); + if ( info!=NULL ) info->bad_cff = true; +return( NULL ); + } else +return( strings[sid-nStdStrings]); +} + +/* I really expect to deal with encodings in ttf cmap, but ocasionally we */ +/* get a bare cff */ +static void readcffenc(FILE *ttf,struct topdicts *dict,struct ttfinfo *info, + char **strings, int scnt) { + int format, cnt, i, j, pos, first, last, dupenc, sid; + extern char *AdobeStandardEncoding[], *AdobeExpertEncoding[]; + const char *name; + EncMap *map; + + if ( info->encoding_start!=0 ) /* Use the cmap instead */ +return; + if ( info->subfontcnt!=0 ) +return; /* Use cids instead */ + + for ( i=0; i<info->glyph_cnt; ++i ) { + if ( info->chars[i]->unicodeenc==-1 ) + info->chars[i]->unicodeenc = UniFromName(info->chars[i]->name,ui_none,&custom); + } + + map = EncMapNew(256,256,&custom); + if ( dict->encodingoff==0 || dict->encodingoff==1 ) { + /* Standard Encodings */ + char **enc = dict->encodingoff==0 ? AdobeStandardEncoding : AdobeExpertEncoding; + map->enc = FindOrMakeEncoding( dict->encodingoff==0 ? + "AdobeStandard" : "Custom" ); + if ( map->enc==NULL ) + map->enc = &custom; + for ( i=0; i<info->glyph_cnt; ++i ) { + for ( pos=0; pos<256; ++pos ) + if ( strcmp(info->chars[i]->name,enc[pos])==0 ) + break; + if ( pos<256 ) + map->map[pos] = i; + } + } else { + fseek(ttf,dict->cff_start+dict->encodingoff,SEEK_SET); + format = getc(ttf); + if ( (format&0x7f)==0 ) { + cnt = getc(ttf); + for ( i=1; i<=cnt && i<info->glyph_cnt; ++i ) + map->map[getc(ttf)] = i; + } else if ( (format&0x7f)==1 ) { + cnt = getc(ttf); + pos = 0; + for ( i=0; i<cnt ; ++i ) { + first = getc(ttf); + last = first + getc(ttf)-1; + while ( first<=last && first<256 ) { + if ( pos<info->glyph_cnt ) + map->map[first] = pos; + ++pos; + ++first; + } + } + } else { + LogError( _("Unexpected encoding format in cff: %d\n"), format ); + if ( info!=NULL ) info->bad_cff = true; + } + if ( format&0x80 ) { + cnt = getc(ttf); + for ( i=0; i<cnt; ++i ) { + dupenc = getc(ttf); + sid = getushort(ttf); + name = getsid(sid,strings,scnt,info); + if ( name==NULL ) /* Table is erroneous */ + break; + for ( j=0; j<info->glyph_cnt; ++j ) + if ( strcmp(name,info->chars[j]->name)==0 ) + break; + if ( j!=info->glyph_cnt ) + map->map[dupenc] = j; + } + } + } + info->map = map; +} + +static void readcffset(FILE *ttf,struct topdicts *dict,struct ttfinfo *info) { + int len = dict->glyphs.cnt; + int i; + int format, cnt, j, first; + + i = 0; + if ( dict->charsetoff==0 ) { + /* ISO Adobe charset */ + dict->charset = galloc(len*sizeof(uint16)); + for ( i=0; i<len && i<=228; ++i ) + dict->charset[i] = i; + } else if ( dict->charsetoff==1 ) { + /* Expert charset */ + dict->charset = galloc((len<162?162:len)*sizeof(uint16)); + dict->charset[0] = 0; /* .notdef */ + dict->charset[1] = 1; + for ( i=2; i<len && i<=238-227; ++i ) + dict->charset[i] = i+227; + dict->charset[12] = 13; + dict->charset[13] = 14; + dict->charset[14] = 15; + dict->charset[15] = 99; + for ( i=16; i<len && i<=248-223; ++i ) + dict->charset[i] = i+223; + dict->charset[25] = 27; + dict->charset[26] = 28; + for ( i=27; i<len && i<=266-222; ++i ) + dict->charset[i] = i+222; + dict->charset[44] = 109; + dict->charset[45] = 110; + for ( i=46; i<len && i<=318-221; ++i ) + dict->charset[i] = i+221; + dict->charset[96] = 158; + dict->charset[97] = 155; + dict->charset[98] = 163; + for ( i=99; i<len && i<=326-220; ++i ) + dict->charset[i] = i+220; + dict->charset[107] = 150; + dict->charset[108] = 164; + dict->charset[109] = 169; + for ( i=110; i<len && i<=378-217; ++i ) + dict->charset[i] = i+217; + } else if ( dict->charsetoff==2 ) { + /* Expert subset charset */ + dict->charset = galloc((len<130?130:len)*sizeof(uint16)); + dict->charset[0] = 0; /* .notdef */ + dict->charset[1] = 1; + for ( i=2; i<len && i<=238-227; ++i ) + dict->charset[i] = i+227; + dict->charset[12] = 13; + dict->charset[13] = 14; + dict->charset[14] = 15; + dict->charset[15] = 99; + for ( i=16; i<len && i<=248-223; ++i ) + dict->charset[i] = i+223; + dict->charset[25] = 27; + dict->charset[26] = 28; + for ( i=27; i<len && i<=266-222; ++i ) + dict->charset[i] = i+222; + dict->charset[44] = 109; + dict->charset[45] = 110; + for ( i=46; i<len && i<=272-221; ++i ) + dict->charset[i] = i+221; + dict->charset[51] = 300; + dict->charset[52] = 301; + dict->charset[53] = 302; + dict->charset[54] = 305; + dict->charset[55] = 314; + dict->charset[56] = 315; + dict->charset[57] = 158; + dict->charset[58] = 155; + dict->charset[59] = 163; + for ( i=60; i<len && i<=326-260; ++i ) + dict->charset[i] = i+260; + dict->charset[67] = 150; + dict->charset[68] = 164; + dict->charset[69] = 169; + for ( i=110; i<len && i<=346-217; ++i ) + dict->charset[i] = i+217; + } else { + dict->charset = galloc(len*sizeof(uint16)); + dict->charset[0] = 0; /* .notdef */ + fseek(ttf,dict->cff_start+dict->charsetoff,SEEK_SET); + format = getc(ttf); + if ( format==0 ) { + for ( i=1; i<len; ++i ) + dict->charset[i] = getushort(ttf); + } else if ( format==1 ) { + for ( i = 1; i<len; ) { + first = dict->charset[i++] = getushort(ttf); + cnt = getc(ttf); + for ( j=0; j<cnt; ++j ) + dict->charset[i++] = ++first; + } + } else if ( format==2 ) { + for ( i = 1; i<len; ) { + first = dict->charset[i++] = getushort(ttf); + cnt = getushort(ttf); + for ( j=0; j<cnt; ++j ) + dict->charset[i++] = ++first; + } + } else { + LogError( _("Unexpected charset format in cff: %d\n"), format ); + if ( info!=NULL ) info->bad_cff = true; + } + } + while ( i<len ) dict->charset[i++] = 0; +} + +static uint8 *readfdselect(FILE *ttf,int numglyphs,struct ttfinfo *info) { + uint8 *fdselect = gcalloc(numglyphs,sizeof(uint8)); + int i, j, format, nr, first, end, fd; + + format = getc(ttf); + if ( format==0 ) { + for ( i=0; i<numglyphs; ++i ) + fdselect[i] = getc(ttf); + } else if ( format==3 ) { + nr = getushort(ttf); + first = getushort(ttf); + for ( i=0; i<nr; ++i ) { + fd = getc(ttf); + end = getushort(ttf); + for ( j=first; j<end; ++j ) { + if ( j>=numglyphs ) { + LogError( _("Bad fdselect\n") ); + if ( info!=NULL ) info->bad_cff = true; + } else + fdselect[j] = fd; + } + first = end; + } + } else { + LogError( _("Didn't understand format for fdselect %d\n"), format ); + if ( info!=NULL ) info->bad_cff = true; + } +return( fdselect ); +} + + +static char *intarray2str(int *array, int size) { + int i,j; + char *pt, *ret; + + for ( i=size-1; i>=0 && array[i]==0; --i ); + if ( i==-1 ) +return( NULL ); + ret = pt = galloc((i+1)*12+12); + *pt++ = '['; + for ( j=0; j<=i; ++j ) { + sprintf( pt, "%d ", array[j]); + pt += strlen(pt); + } + pt[-1]=']'; +return( ret ); +} + +static char *realarray2str(real *array, int size, int must_be_even) { + int i,j; + char *pt, *ret; + + for ( i=size-1; i>=0 && array[i]==0; --i ); + if ( i==-1 ) +return( NULL ); + if ( must_be_even && !(i&1) && array[i]<0 ) + ++i; /* Someone gave us a bluevalues of [-20 0] and we reported [-20] */ + ret = pt = galloc((i+1)*20+12); + *pt++ = '['; + for ( j=0; j<=i; ++j ) { + sprintf( pt, "%g ", (double) array[j]); + pt += strlen(pt); + } + pt[-1]=']'; +return( ret ); +} + +static void privateadd(struct psdict *private,char *key,char *value) { + if ( value==NULL ) +return; + private->keys[private->next] = copy(key); + private->values[private->next++] = value; +} + +static void privateaddint(struct psdict *private,char *key,int val) { + char buf[10]; + if ( val==0 ) +return; + sprintf( buf,"%d", val ); + privateadd(private,key,copy(buf)); +} + +static void privateaddintarray(struct psdict *private,char *key,int val) { + char buf[10]; + if ( val==0 ) +return; + sprintf( buf,"[%d]", val ); + privateadd(private,key,copy(buf)); +} + +static void privateaddreal(struct psdict *private,char *key,double val,double def) { + char buf[10]; + if ( val==def ) +return; + sprintf( buf,"%g", val ); + privateadd(private,key,copy(buf)); +} + +static void cffprivatefillup(struct psdict *private, struct topdicts *dict) { + private->cnt = 14; + private->keys = galloc(14*sizeof(char *)); + private->values = galloc(14*sizeof(char *)); + privateadd(private,"BlueValues", + realarray2str(dict->bluevalues,sizeof(dict->bluevalues)/sizeof(dict->bluevalues[0]),true)); + privateadd(private,"OtherBlues", + realarray2str(dict->otherblues,sizeof(dict->otherblues)/sizeof(dict->otherblues[0]),true)); + privateadd(private,"FamilyBlues", + realarray2str(dict->familyblues,sizeof(dict->familyblues)/sizeof(dict->familyblues[0]),true)); + privateadd(private,"FamilyOtherBlues", + realarray2str(dict->familyotherblues,sizeof(dict->familyotherblues)/sizeof(dict->familyotherblues[0]),true)); + privateaddreal(private,"BlueScale",dict->bluescale,0.039625); + privateaddreal(private,"BlueShift",dict->blueshift,7); + privateaddreal(private,"BlueFuzz",dict->bluefuzz,1); + privateaddintarray(private,"StdHW",dict->stdhw); + privateaddintarray(private,"StdVW",dict->stdvw); + privateadd(private,"StemSnapH", + realarray2str(dict->stemsnaph,sizeof(dict->stemsnaph)/sizeof(dict->stemsnaph[0]),false)); + privateadd(private,"StemSnapV", + realarray2str(dict->stemsnapv,sizeof(dict->stemsnapv)/sizeof(dict->stemsnapv[0]),false)); + if ( dict->forcebold ) + privateadd(private,"ForceBold",copy("true")); + if ( dict->forceboldthreshold!=0 ) + privateaddreal(private,"ForceBoldThreshold",dict->forceboldthreshold,0); + privateaddint(private,"LanguageGroup",dict->languagegroup); + privateaddreal(private,"ExpansionFactor",dict->expansionfactor,0.06); +} + +static SplineFont *cffsffillup(struct topdicts *subdict, char **strings, + int scnt, struct ttfinfo *info) { + SplineFont *sf = SplineFontEmpty(); + int emsize; + static int nameless; + + sf->fontname = utf8_verify_copy(getsid(subdict->sid_fontname,strings,scnt,info)); + if ( sf->fontname==NULL ) { + char buffer[40]; + sprintf(buffer,"UntitledSubFont_%d", ++nameless ); + sf->fontname = copy(buffer); + } + + if ( subdict->fontmatrix[0]==0 ) + emsize = 1000; + else + emsize = rint( 1/subdict->fontmatrix[0] ); + sf->ascent = .8*emsize; + sf->descent = emsize - sf->ascent; + if ( subdict->copyright!=-1 ) + sf->copyright = utf8_verify_copy(getsid(subdict->copyright,strings,scnt,info)); + else + sf->copyright = utf8_verify_copy(getsid(subdict->notice,strings,scnt,info)); + sf->familyname = utf8_verify_copy(getsid(subdict->familyname,strings,scnt,info)); + sf->fullname = utf8_verify_copy(getsid(subdict->fullname,strings,scnt,info)); + sf->weight = utf8_verify_copy(getsid(subdict->weight,strings,scnt,info)); + sf->version = utf8_verify_copy(getsid(subdict->version,strings,scnt,info)); + sf->italicangle = subdict->italicangle; + sf->upos = subdict->underlinepos; + sf->uwidth = subdict->underlinewidth; + sf->xuid = intarray2str(subdict->xuid,sizeof(subdict->xuid)/sizeof(subdict->xuid[0])); + sf->uniqueid = subdict->uniqueid; + sf->strokewidth = subdict->strokewidth; + sf->strokedfont = subdict->painttype==2; + + if ( subdict->private_size>0 ) { + sf->private = gcalloc(1,sizeof(struct psdict)); + cffprivatefillup(sf->private,subdict); + } +return( sf ); +} + +static void cffinfofillup(struct ttfinfo *info, struct topdicts *dict, + char **strings, int scnt ) { + + info->glyph_cnt = dict->glyphs.cnt; + if ( info->glyph_cnt<0 ) info->glyph_cnt = 0; + + if ( dict->fontmatrix[0]==0 ) + info->emsize = 1000; + else + info->emsize = rint( 1/dict->fontmatrix[0] ); +#if 1 + info->ascent = .8*info->emsize; +#else + info->ascent = dict->fontbb[3]*info->emsize/(dict->fontbb[3]-dict->fontbb[1]); +#endif + info->descent = info->emsize - info->ascent; + if ( dict->copyright!=-1 || dict->notice!=-1 ) + free( info->copyright ); + if ( dict->copyright!=-1 ) + info->copyright = utf8_verify_copy(getsid(dict->copyright,strings,scnt,info)); + else if ( dict->notice!=-1 ) + info->copyright = utf8_verify_copy(getsid(dict->notice,strings,scnt,info)); + if ( dict->familyname!=-1 ) { + free(info->familyname); + info->familyname = utf8_verify_copy(getsid(dict->familyname,strings,scnt,info)); + } + if ( dict->fullname!=-1 ) { + free(info->fullname); + info->fullname = utf8_verify_copy(getsid(dict->fullname,strings,scnt,info)); + } + if ( dict->weight!=-1 ) { + free(info->weight); + info->weight = utf8_verify_copy(getsid(dict->weight,strings,scnt,info)); + } + if ( dict->version!=-1 ) { + free(info->version); + info->version = utf8_verify_copy(getsid(dict->version,strings,scnt,info)); + } + if ( dict->fontname!=NULL ) { + free(info->fontname); + info->fontname = utf8_verify_copy(dict->fontname); + } + info->italicAngle = dict->italicangle; + info->upos = dict->underlinepos; + info->uwidth = dict->underlinewidth; + info->xuid = intarray2str(dict->xuid,sizeof(dict->xuid)/sizeof(dict->xuid[0])); + info->uniqueid = dict->uniqueid; + info->strokewidth = dict->strokewidth; + info->strokedfont = dict->painttype==2; + + if ( dict->private_size>0 ) { + info->private = gcalloc(1,sizeof(struct psdict)); + cffprivatefillup(info->private,dict); + } + if ( dict->ros_registry!=-1 ) { + info->cidregistry = copy(getsid(dict->ros_registry,strings,scnt,info)); + info->ordering = copy(getsid(dict->ros_ordering,strings,scnt,info)); + info->supplement = dict->ros_supplement; + info->cidfontversion = dict->cidfontversion; + } +} + +static void cfffigure(struct ttfinfo *info, struct topdicts *dict, + char **strings, int scnt, struct pschars *gsubrs) { + int i, cstype; + struct pschars *subrs; + struct pscontext pscontext; + memset(&pscontext,0,sizeof(pscontext)); + + cffinfofillup(info, dict, strings, scnt ); + +/* The format allows for some dicts that are type1 strings and others that */ +/* are type2s. Which means that the global subrs will have a different bias */ +/* as we flip from font to font. So we can't set the bias when we read in */ +/* the subrs but must wait until we know which font we're working on. */ + cstype = dict->charstringtype; + pscontext.is_type2 = cstype-1; + pscontext.painttype = dict->painttype; + gsubrs->bias = cstype==1 ? 0 : + gsubrs->cnt < 1240 ? 107 : + gsubrs->cnt <33900 ? 1131 : 32768; + subrs = &dict->local_subrs; + subrs->bias = cstype==1 ? 0 : + subrs->cnt < 1240 ? 107 : + subrs->cnt <33900 ? 1131 : 32768; + + info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *)); + for ( i=0; i<info->glyph_cnt; ++i ) { + info->chars[i] = PSCharStringToSplines( + dict->glyphs.values[i], dict->glyphs.lens[i],&pscontext, + subrs,gsubrs,getsid(dict->charset[i],strings,scnt,info)); + info->chars[i]->vwidth = info->emsize; + if ( cstype==2 ) { + if ( info->chars[i]->width == (int16) 0x8000 ) + info->chars[i]->width = dict->defaultwidthx; + else + info->chars[i]->width += dict->nominalwidthx; + } + } + /* Need to do a reference fixup here !!!!! just in case some idiot */ + /* used type1 char strings -- or used the depreciated meaning of */ + /* endchar (==seac) */ +} + +static void cidfigure(struct ttfinfo *info, struct topdicts *dict, + char **strings, int scnt, struct pschars *gsubrs, struct topdicts **subdicts, + uint8 *fdselect) { + int i, j, cstype, uni, cid; + struct pschars *subrs; + SplineFont *sf; + struct cidmap *map; + char buffer[100]; + struct pscontext pscontext; + EncMap *encmap = NULL; + + memset(&pscontext,0,sizeof(pscontext)); + + cffinfofillup(info, dict, strings, scnt ); + + /* We'll set the encmap later */ + /*info->map = encmap = EncMapNew(info->glyph_cnt,info->glyph_cnt,&custom);*/ + + for ( j=0; subdicts[j]!=NULL; ++j ); + info->subfontcnt = j; + info->subfonts = gcalloc(j+1,sizeof(SplineFont *)); + for ( j=0; subdicts[j]!=NULL; ++j ) { + info->subfonts[j] = cffsffillup(subdicts[j],strings,scnt,info); + info->subfonts[j]->map = encmap; + } + for ( i=0; i<info->glyph_cnt; ++i ) { + sf = info->subfonts[ fdselect[i] ]; + cid = dict->charset[i]; + if ( cid>=sf->glyphcnt ) sf->glyphcnt = sf->glyphmax = cid+1; + /*if ( cid>=encmap->enccount ) encmap->enccount = cid+1;*/ + } + for ( j=0; subdicts[j]!=NULL; ++j ) + info->subfonts[j]->glyphs = gcalloc(info->subfonts[j]->glyphcnt,sizeof(SplineChar *)); + /*encmap->encmax = encmap->enccount;*/ + /*encmap->map = galloc(encmap->enccount*sizeof(int));*/ + /*memset(encmap->map,-1,encmap->enccount*sizeof(int));*/ + + info->chars = gcalloc(info->glyph_cnt,sizeof(SplineChar *)); + + /* info->chars provides access to the chars ordered by glyph, which the */ + /* ttf routines care about */ + /* sf->glyphs provides access to the chars ordered by CID. Not sure what */ + /* would happen to a kern from one font to another... */ + + map = FindCidMap(info->cidregistry,info->ordering,info->supplement,NULL); + + for ( i=0; i<info->glyph_cnt; ++i ) { + j = fdselect[i]; + sf = info->subfonts[ j ]; +/* The format allows for some dicts that are type1 strings and others that */ +/* are type2s. Which means that the global subrs will have a different bias */ +/* as we flip from font to font. So we can't set the bias when we read in */ +/* the subrs but must wait until we know which font we're working on. */ + cstype = subdicts[j]->charstringtype; + pscontext.is_type2 = cstype-1; + pscontext.painttype = subdicts[j]->painttype; + gsubrs->bias = cstype==1 ? 0 : + gsubrs->cnt < 1240 ? 107 : + gsubrs->cnt <33900 ? 1131 : 32768; + subrs = &subdicts[j]->local_subrs; + subrs->bias = cstype==1 ? 0 : + subrs->cnt < 1240 ? 107 : + subrs->cnt <33900 ? 1131 : 32768; + + cid = dict->charset[i]; + /*encmap->map[cid] = cid;*/ + uni = CID2NameUni(map,cid,buffer,sizeof(buffer)); + info->chars[i] = PSCharStringToSplines( + dict->glyphs.values[i], dict->glyphs.lens[i],&pscontext, + subrs,gsubrs,buffer); + info->chars[i]->vwidth = sf->ascent+sf->descent; + info->chars[i]->unicodeenc = uni; + sf->glyphs[cid] = info->chars[i]; + sf->glyphs[cid]->parent = sf; + sf->glyphs[cid]->orig_pos = cid; /* Bug! should be i, but I assume sf->chars[orig_pos]->orig_pos==orig_pos */ + if ( sf->glyphs[cid]->layers[ly_fore].refs!=NULL ) + IError( "Reference found in CID font. Can't fix it up"); + + THPatchSplineChar(sf->glyphs[cid]); + + if ( cstype==2 ) { + if ( sf->glyphs[cid]->width == (int16) 0x8000 ) + sf->glyphs[cid]->width = subdicts[j]->defaultwidthx; + else + sf->glyphs[cid]->width += subdicts[j]->nominalwidthx; + } + ff_progress_next(); + } + /* No need to do a reference fixup here-- the chars aren't associated */ + /* with any encoding as is required for seac */ +} + +static int readcffglyphs(FILE *ttf,struct ttfinfo *info) { + int offsize; + int hdrsize; + char **fontnames, **strings; + struct topdicts **dicts, **subdicts; + int i, j, which; + struct pschars gsubs; + uint8 *fdselect; + int scnt; + + fseek(ttf,info->cff_start,SEEK_SET); + if ( getc(ttf)!='\1' ) { /* Major version */ + LogError( _("CFF version mismatch\n" )); + info->bad_cff = true; +return( 0 ); + } + getc(ttf); /* Minor version */ + hdrsize = getc(ttf); + offsize = getc(ttf); + if ( hdrsize!=4 ) + fseek(ttf,info->cff_start+hdrsize,SEEK_SET); + fontnames = readcfffontnames(ttf,NULL,info); + which = 0; + if ( fontnames[1]!=NULL ) { /* More than one? Can that even happen in OpenType? */ + which = PickCFFFont(fontnames); + if ( which==-1 ) { + for ( i=0; fontnames[i]!=NULL; ++i ) + free(fontnames[i]); + free(fontnames); +return( 0 ); + } + } + dicts = readcfftopdicts(ttf,fontnames,info->cff_start,info, NULL); + /* String index is just the same as fontname index */ + strings = readcfffontnames(ttf,&scnt,info); + readcffsubrs(ttf,&gsubs,info ); + /* Can be many fonts here. Only decompose the one */ + if ( dicts[which]->charstringsoff!=-1 ) { + fseek(ttf,info->cff_start+dicts[which]->charstringsoff,SEEK_SET); + readcffsubrs(ttf,&dicts[which]->glyphs,info); + } + if ( dicts[which]->private_offset!=-1 ) + readcffprivate(ttf,dicts[which],info); + if ( dicts[which]->charsetoff!=-1 ) + readcffset(ttf,dicts[which],info); + if ( dicts[which]->fdarrayoff==-1 ) + cfffigure(info,dicts[which],strings,scnt,&gsubs); + else { + fseek(ttf,info->cff_start+dicts[which]->fdarrayoff,SEEK_SET); + subdicts = readcfftopdicts(ttf,NULL,info->cff_start,info,dicts[which]); + fseek(ttf,info->cff_start+dicts[which]->fdselectoff,SEEK_SET); + fdselect = readfdselect(ttf,dicts[which]->glyphs.cnt,info); + for ( j=0; subdicts[j]!=NULL; ++j ) { + if ( subdicts[j]->private_offset!=-1 ) + readcffprivate(ttf,subdicts[j],info); + if ( subdicts[j]->charsetoff!=-1 ) + readcffset(ttf,subdicts[j],info); + } + cidfigure(info,dicts[which],strings,scnt,&gsubs,subdicts,fdselect); + for ( j=0; subdicts[j]!=NULL; ++j ) + TopDictFree(subdicts[j]); + free(subdicts); free(fdselect); + } + if ( dicts[which]->encodingoff!=-1 ) + readcffenc(ttf,dicts[which],info,strings,scnt); + + if ( dicts[which]->fdarrayoff==-1 ) { + for ( i=0; i<info->glyph_cnt ; ++i ) + if ( info->chars[i]!=NULL ) + info->chars[i]->orig_pos = i; + } + + if ( info->to_order2 ) { + for ( i=0; i<info->glyph_cnt; ++i ) + SCConvertToOrder2(info->chars[i]); + } + + for ( i=0; fontnames[i]!=NULL && i<1; ++i ) { + free(fontnames[i]); + TopDictFree(dicts[i]); + } + free(fontnames); free(dicts); + if ( strings!=NULL ) { + for ( i=0; strings[i]!=NULL; ++i ) + free(strings[i]); + free(strings); + } + for ( i=0; i<gsubs.cnt; ++i ) + free(gsubs.values[i]); + free(gsubs.values); free(gsubs.lens); + +return( 1 ); +} + +static int readtyp1glyphs(FILE *ttf,struct ttfinfo *info) { + FontDict *fd; + FILE *tmp; + int i; + SplineChar *sc; + + fseek(ttf,info->typ1_start,SEEK_SET); +/* There appear to be about 20 bytes of garbage (well, I don't know what they */ +/* mean, so they are garbage to me) before the start of the PostScript. But */ +/* it's not exactly 20. I've seen 22 and 24. So see if we can find "%!PS-Adobe" */ +/* in the first few bytes of the file, and skip to there if found */ + { char buffer[41]; + fread(buffer,1,sizeof(buffer),ttf); + buffer[40] = '\0'; + for ( i=39; i>=0; --i ) + if ( buffer[i]=='%' && buffer[i+1]=='!' ) + break; + if ( i<0 ) + i = 0; + fseek(ttf,info->typ1_start+i,SEEK_SET); + } + + tmp = tmpfile(); + for ( i=0; i<(int)info->typ1_length; ++i ) + putc(getc(ttf),tmp); + rewind(tmp); + fd = _ReadPSFont(tmp); + fclose(tmp); + if ( fd!=NULL ) { + SplineFont *sf = SplineFontFromPSFont(fd); + PSFontFree(fd); + info->emsize = (sf->ascent+sf->descent); + info->ascent = sf->ascent; + info->descent = sf->descent; + if ( sf->subfontcnt!=0 ) { + info->subfontcnt = sf->subfontcnt; + info->subfonts = sf->subfonts; + info->cidregistry = copy(sf->cidregistry); + info->ordering = copy(sf->ordering); + info->supplement = sf->supplement; + info->cidfontversion = sf->cidversion; + sf->subfonts = NULL; + sf->subfontcnt = 0; + } else { + info->chars = sf->glyphs; + info->glyph_cnt = sf->glyphcnt; + for ( i=sf->glyphcnt-1; i>=0; --i ) if ( (sc=sf->glyphs[i])!=NULL ) + sc->parent = NULL; + sf->glyphs = NULL; + sf->glyphcnt = 0; + } + SplineFontFree(sf); +return( true ); + } +return( false ); +} + +static void readttfwidths(FILE *ttf,struct ttfinfo *info) { + int i,j; + int lastwidth = info->emsize, lsb; + /* I'm not interested in the lsb, I'm not sure what it means if it differs*/ + /* from that is specified in the outline. Do we move the outline? */ + /* Ah... I am interested in it if bit 1 of 'head'.flags is set, then we */ + /* do move the outline */ + int check_width_consistency = info->cff_start!=0 && info->glyph_start==0; + SplineChar *sc; + real trans[6]; + + memset(trans,0,sizeof(trans)); + trans[0] = trans[3] = 1; + + fseek(ttf,info->hmetrics_start,SEEK_SET); + for ( i=0; i<info->width_cnt && i<info->glyph_cnt; ++i ) { + lastwidth = getushort(ttf); + lsb = (short) getushort(ttf); + if ( (sc = info->chars[i])!=NULL ) { /* can happen in ttc files */ + if ( check_width_consistency && sc->width!=lastwidth ) { + if ( info->fontname!=NULL && sc->name!=NULL ) + LogError("In %s, in glyph %s, 'CFF ' advance width (%d) and\n 'hmtx' width (%d) do not match. (Subsequent mismatches will not be reported)\n", + info->fontname, sc->name, sc->width, lastwidth ); + else + LogError("In GID %d, 'CFF ' advance width (%d) and 'hmtx' width (%d) do not match.\n (Subsequent mismatches will not be reported)\n", + i, sc->width, lastwidth ); + info->bad_metrics = true; + check_width_consistency = false; + } + sc->width = lastwidth; + sc->widthset = true; + if ( info->apply_lsb ) { + if ( sc->lsidebearing!=lsb ) { + trans[4] = lsb-sc->lsidebearing; + SplinePointListTransform(sc->layers[ly_fore].splines,trans,true); + } + } + } + } + if ( i==0 ) { + LogError( _("Invalid ttf hmtx table (or hhea), numOfLongMetrics is 0\n") ); + info->bad_metrics = true; + } + + for ( j=i; j<info->glyph_cnt; ++j ) { + if ( (sc = info->chars[j])!=NULL ) { /* In a ttc file we may skip some */ + sc->width = lastwidth; + sc->widthset = true; + if ( info->apply_lsb ) { + lsb = (short) getushort(ttf); + if ( sc->lsidebearing!=lsb ) { + trans[4] = lsb-sc->lsidebearing; + SplinePointListTransform(sc->layers[ly_fore].splines,trans,true); + } + } + } + } +} + +static void dummywidthsfromstrike(FILE *ttf,struct ttfinfo *info) { + BDFFont *bdf; + int i, cnt; + double scaled_sum; + (void)ttf; /* -Wall */ + if ( info->bitmaps==NULL ) +return; +#if 0 + for ( bdf=info->bitmaps; bdf->next!=NULL; bdf=bdf->next ); + for ( i=0; i<bdf->glyphcnt; ++i ) if ( bdf->glyphs[i]!=NULL ) { + bdf->glyphs[i]->sc->width = info->emsize*bdf->glyphs[i]->width/bdf->pixelsize; + bdf->glyphs[i]->sc->widthset = true; + } +#else + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) { + cnt = 0; scaled_sum = 0; + for ( bdf=info->bitmaps; bdf->next!=NULL; bdf=bdf->next ) { + if ( i<bdf->glyphcnt && bdf->glyphs[i]!=NULL ) { + scaled_sum += ((double) (info->emsize*bdf->glyphs[i]->width))/bdf->pixelsize; + ++cnt; + } + } + if ( cnt!=0 ) { + info->chars[i]->width = scaled_sum/cnt; + info->chars[i]->widthset = true; + } + } +#endif +} + +static void readttfvwidths(FILE *ttf,struct ttfinfo *info) { + int i,j; + int lastvwidth = info->emsize, vwidth_cnt, tsb/*, cnt=0*/; + /* int32 voff=0; */ + + fseek(ttf,info->vhea_start+4+4,SEEK_SET); /* skip over the version number & typo right/left */ + info->pfminfo.vlinegap = getushort(ttf); + info->pfminfo.vheadset = true; + + for ( i=0; i<12; ++i ) + getushort(ttf); + vwidth_cnt = getushort(ttf); + + fseek(ttf,info->vmetrics_start,SEEK_SET); + for ( i=0; i<vwidth_cnt && i<info->glyph_cnt; ++i ) { + lastvwidth = getushort(ttf); + tsb = getushort(ttf); + if ( info->chars[i]!=NULL ) { /* can happen in ttc files */ + info->chars[i]->vwidth = lastvwidth; +#if 0 /* this used to mean something once */ + /* At one point I stored the ymax of loading the glyph in lsidebearing*/ + /* I've removed that as it now seems pointless */ + if ( info->cff_start==0 ) { + voff += tsb + info->chars[i]->lsidebearing /* actually maxy */; + ++cnt; + } +#endif + } + } + if ( i==0 ) { + LogError( _("Invalid ttf vmtx table (or vhea), numOfLongVerMetrics is 0\n") ); + info->bad_metrics = true; + } + + for ( j=i; j<info->glyph_cnt; ++j ) { + if ( info->chars[j]!=NULL ) /* In a ttc file we may skip some */ + info->chars[j]->vwidth = lastvwidth; + } + +} + +static int modenc(int enc,int modtype) { + (void)modtype; /* for -Wall */ +return( enc ); +} + +static int badencoding(struct ttfinfo *info) { + if ( !info->bad_cmap ) { + LogError("Bad encoding information in 'cmap' table."); + info->bad_cmap = true; + } +return( -1 ); +} + +static int umodenc(int enc,int modtype, struct ttfinfo *info) { + if ( modtype==-1 ) +return( -1 ); + if ( modtype<=1 /* Unicode */ ) { + /* No conversion needed, already unicode */; + } else if ( modtype==2 /* SJIS */ ) { + if ( enc<=127 ) { + /* Latin */ + if ( enc=='\\' ) enc = 0xa5; /* Yen */ + } else if ( enc>=161 && enc<=223 ) { + /* Katakana */ + enc = unicode_from_jis201[enc]; + } else if ( enc<255 ) { + /* This is erroneous as I understand SJIS */ + enc = badencoding(info); + } else { + int ch1 = enc>>8, ch2 = enc&0xff; + if ( ch1 >= 129 && ch1<= 159 ) + ch1 -= 112; + else + ch1 -= 176; + ch1 <<= 1; + if ( ch2>=159 ) + ch2-= 126; + else if ( ch2>127 ) { + --ch1; + ch2 -= 32; + } else { + --ch1; + ch2 -= 31; + } + if ( ch1<0x21 || ch2<0x21 || ch1>0x7f || ch2>0x7f ) + enc = badencoding(info); + else + enc = unicode_from_jis208[(ch1-0x21)*94+(ch2-0x21)]; + } + } else if ( modtype==3 /* GB2312 offset by 0x8080, parse just like wansung */ ) { + if ( enc>0xa1a1 ) { + enc -= 0xa1a1; + enc = (enc>>8)*94 + (enc&0xff); + enc = unicode_from_gb2312[enc]; + if ( enc==0 ) enc = -1; + } else if ( enc>0x100 ) + enc = badencoding(info); + } else if ( modtype==4 /* BIG5 */ ) { /* old ms docs say big5 is modtype==3, but new ones say 4 */ + if ( enc>0x8100 ) + enc = unicode_from_big5hkscs[enc-0x8100]; + else if ( enc>0x100 ) + enc = badencoding(info); + } else if ( modtype==5 /* Wansung == KSC 5601-1987, I hope */ ) { + if ( enc>0xa1a1 ) { + enc -= 0xa1a1; + enc = (enc>>8)*94 + (enc&0xff); + enc = unicode_from_ksc5601[enc]; + if ( enc==0 ) enc = -1; + } else if ( enc>0x100 ) + enc = badencoding(info); + } else if ( modtype==6 /* Johab */ ) { + if ( enc>0x8400 ) + enc = unicode_from_johab[enc-0x8400]; + else if ( enc>0x100 ) + enc = badencoding(info); + } + if ( enc==0 ) + enc = -1; +return( enc ); +} + +struct cmap_encs { + int platform; + int specific; + int offset; + int lang; + int format; + Encoding *enc; +}; + +static int SubtableIsntSupported(FILE *ttf,uint32 offset,struct cmap_encs *cmap_enc, struct ttfinfo *info) { + uint32 here = ftell(ttf); + int format, len, ret=false; + + fseek(ttf,offset,SEEK_SET); + + cmap_enc->format = format = getushort(ttf); + if ( format<0 || (format&1) || format>12 ) { + LogError( _("Encoding subtable for platform=%d, specific=%d has an unsupported format %d.\n"), + cmap_enc->platform, cmap_enc->specific, format ); + info->bad_cmap = true; + ret = true; + } + + if ( format!=12 && format!=10 && format!=8 ) { + len = getushort(ttf); + cmap_enc->lang = getushort(ttf); + } else { + /* padding */ getushort(ttf); + len = getlong(ttf); + cmap_enc->lang = getlong(ttf); + } + if ( len==0 ) { + LogError( _("Encoding subtable for platform=%d, specific=%d has a 0 length subtable.\n"), + cmap_enc->platform, cmap_enc->specific ); + info->bad_cmap = true; + ret = true; + } + fseek(ttf,here,SEEK_SET); +return( ret ); +} + +static int SubtableMustBe14(FILE *ttf,uint32 offset,struct ttfinfo *info) { + uint32 here = ftell(ttf); + int format, ret=true; + + fseek(ttf,offset,SEEK_SET); + + format = getushort(ttf); + if ( format!=14 ) { + LogError( _("Encoding subtable for platform=%d, specific=%d (which must be 14)\nhas an unsupported format %d.\n"), + 0, 5, format ); + info->bad_cmap = true; + ret = false; + } + fseek(ttf,here,SEEK_SET); +return( ret ); +} + +static void ApplyVariationSequenceSubtable(FILE *ttf,uint32 vs_map, + struct ttfinfo *info,int justinuse) { + int sub_table_len, vs_cnt, i, j, rcnt, gid; + struct vs_data { int vs; uint32 def, non_def; } *vs_data; + SplineChar *sc; + + fseek(ttf,vs_map,SEEK_SET); + /* We/ve already checked the format is 14 */ getushort(ttf); + sub_table_len = getlong(ttf); + vs_cnt = getlong(ttf); + vs_data = galloc(vs_cnt*sizeof(struct vs_data)); + for ( i=0; i<vs_cnt; ++i ) { + vs_data[i].vs = get3byte(ttf); + vs_data[i].def = getlong(ttf); + vs_data[i].non_def = getlong(ttf); + } + + for ( i=0; i<vs_cnt; ++i ) { + if ( vs_data[i].def!=0 && justinuse==git_normal ) { + fseek(ttf,vs_map+vs_data[i].def,SEEK_SET); + rcnt = getlong(ttf); + for ( j=0; j<rcnt; ++j ) { + int start_uni = get3byte(ttf); + int cnt = getc(ttf); + int uni; + for ( uni=start_uni; uni<=start_uni+cnt; ++uni ) { + SplineChar *sc=NULL; + struct altuni *altuni; + for ( gid = 0; gid<info->glyph_cnt; ++gid ) { + if ( (sc = info->chars[gid])!=NULL ) { + if ( sc->unicodeenc==uni ) + break; + for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next ) + if ( altuni->unienc==uni && altuni->vs == -1 && altuni->fid==0 ) + break; + if ( altuni!=NULL ) + break; + } + } + if ( gid==info->glyph_cnt ) { + LogError( _("No glyph with unicode U+%05x in font\n"), + uni ); + info->bad_cmap = true; + } else { + altuni = chunkalloc(sizeof(struct altuni)); + altuni->unienc = uni; + altuni->vs = vs_data[i].vs; + altuni->fid = 0; + altuni->next = sc->altuni; + sc->altuni = altuni; + } + } + } + } + if ( vs_data[i].non_def!=0 ) { + fseek(ttf,vs_map+vs_data[i].non_def,SEEK_SET); + rcnt = getlong(ttf); + for ( j=0; j<rcnt; ++j ) { + int uni = get3byte(ttf); + int curgid = getushort(ttf); + if ( justinuse==git_justinuse ) { + if ( curgid<info->glyph_cnt && curgid>=0) + info->inuse[curgid] = 1; + } else if ( justinuse==git_justinuse ) { + if ( curgid<info->glyph_cnt && curgid>=0 && + (sc=info->chars[curgid])!=NULL && sc->name==NULL ) { + char buffer[32]; + sprintf(buffer, "u%04X.vs%04X", uni, vs_data[i].vs ); + sc->name = copy(buffer); + } + } else { + if ( curgid>=info->glyph_cnt || curgid<0 || + info->chars[curgid]==NULL ) { + LogError( _("GID out of range (%d) in format 14 'cmap' subtable\n"), + curgid ); + info->bad_cmap = true; + } else { + SplineChar *sc = info->chars[curgid]; + struct altuni *altuni = chunkalloc(sizeof(struct altuni)); + altuni->unienc = uni; + altuni->vs = vs_data[i].vs; + altuni->fid = 0; + altuni->next = sc->altuni; + sc->altuni = altuni; + } + } + } + } + } +} + +static enum uni_interp amscheck(struct ttfinfo *info, EncMap *map) { + int cnt = 0; + /* Try to guess if the font uses the AMS math PUA assignments */ + + if ( map==NULL ) +return( ui_none ); + + if ( 0xe668<map->enccount && map->map[0xe668]!=-1 && + info->chars[map->map[0xe668]]->unicodeenc=='b' ) + ++cnt; + if ( 0xe3c8<map->enccount && map->map[0xe626]!=-1 && + info->chars[map->map[0xe626]]->unicodeenc==0xe626 ) + ++cnt; + if ( 0xe3c8<map->enccount && map->map[0xe3c8]!=-1 && + info->chars[map->map[0xe3c8]]->unicodeenc==0x29e1 ) + ++cnt; + if ( 0x2A7C<map->enccount && map->map[0x2A7C]!=-1 && + info->chars[map->map[0x2A7C]]->unicodeenc==0xE32A ) + ++cnt; + if ( 0x2920<map->enccount && map->map[0x2920]!=-1 && + info->chars[map->map[0x2920]]->unicodeenc==0xE221 ) + ++cnt; +return( cnt>=2 ? ui_ams : ui_none ); +} + +static int PickCMap(struct cmap_encs *cmap_encs,int enccnt,int def) { + char buffer[500]; + char **choices, *encname; + int i, ret; + static char *macscripts[]= { N_("Script|Roman"), N_("Script|Japanese"), N_("Script|Traditional Chinese"), N_("Script|Korean"), + N_("Script|Arabic"), N_("Script|Hebrew"), N_("Script|Greek"), +/* GT: Don't ask me what RSymbol means, I don't know either. It's in apple's */ +/* GT: docs though */ + N_("Script|Cyrillic"), N_("Script|RSymbol"), N_("Script|Devanagari"), +/* 10*/ N_("Script|Gurmukhi"), N_("Script|Gujarati"), NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, +/* 20*/ NULL, N_("Script|Thai"), NULL, NULL, NULL, N_("Script|Simplified Chinese"), + NULL, NULL, NULL, N_("Script|Central European"), +/* 30*/ NULL, NULL, NULL }; + + choices = galloc(enccnt*sizeof(char *)); + for ( i=0; i<enccnt; ++i ) { + encname = NULL; + if ( cmap_encs[i].platform==1 && cmap_encs[i].specific<32 ) { + encname = macscripts[cmap_encs[i].specific]; + if ( encname!=NULL ) + encname = S_(encname); + } else if ( cmap_encs[i].platform==0 ) { + switch ( cmap_encs[i].specific ) { + case 0: + encname = N_("Unicode 1.0"); + break; + case 1: + encname = N_("Unicode 1.1"); + break; + case 2: + encname = N_("ISO 10646:1993"); + break; + case 3: + encname = N_("Unicode 2.0+, BMP only"); + break; + case 4: + encname = N_("Unicode 2.0+, all planes"); + break; + } + } else if ( cmap_encs[i].platform==3 && cmap_encs[i].specific==0 ) + encname = N_("\"Symbol\""); + if ( encname==NULL ) + encname = cmap_encs[i].enc->enc_name; + + sprintf(buffer,"%d (%s) %d %s %s %s", + cmap_encs[i].platform, + cmap_encs[i].platform==0 ? _("Unicode") : + cmap_encs[i].platform==1 ? _("Apple") : + cmap_encs[i].platform==2 ? _("ISO (Deprecated)") : + cmap_encs[i].platform==3 ? _("MicroSoft") : + cmap_encs[i].platform==4 ? _("Custom") : + cmap_encs[i].platform==7 ? _("FreeType internals") : + _("Unknown"), + cmap_encs[i].specific, + encname, + cmap_encs[i].platform==1 && cmap_encs[i].lang!=0? MacLanguageFromCode(cmap_encs[i].lang-1) : "", + cmap_encs[i].format == 0 ? "Byte encoding table" : + cmap_encs[i].format == 2 ? "High-byte mapping through table" : + cmap_encs[i].format == 4 ? "Segment mapping to delta values" : + cmap_encs[i].format == 6 ? "Trimmed table mapping" : + cmap_encs[i].format == 8 ? "mixed 16-bit and 32-bit coverage" : + cmap_encs[i].format == 10 ? "Trimmed array" : + cmap_encs[i].format == 12 ? "Segmented coverage" : + "Unknown format" ); + choices[i] = copy(buffer); + } + ret = ff_choose(_("Pick a CMap subtable"),(const char **) choices,enccnt,def, + _("Pick a CMap subtable")); + for ( i=0; i<enccnt; ++i ) + free(choices[i]); + free(choices); +return( ret ); +} + +/* 'cmap' table: readttfcmap */ +static void readttfencodings(FILE *ttf,struct ttfinfo *info, int justinuse) { + int i,j, def, unicode_cmap, unicode4_cmap, dcnt, dcmap_cnt, dc; + int nencs, version, usable_encs; + Encoding *enc = &custom; + const int32 *trans=NULL; + enum uni_interp interp = ui_none; + int platform, specific; + int offset, encoff=0; + int format, len; + uint32 vs_map=0; + uint16 table[256]; + int segCount; + uint16 *endchars, *startchars, *delta, *rangeOffset, *glyphs; + int index, last; + int mod = 0; + SplineChar *sc; + uint8 *used; + int badencwarned=false; + int glyph_tot; + Encoding *temp; + EncMap *map; + struct cmap_encs *cmap_encs, desired_cmaps[2], *dcmap; + extern int ask_user_for_cmap; + + fseek(ttf,info->encoding_start,SEEK_SET); + version = getushort(ttf); + nencs = getushort(ttf); + if ( version!=0 && nencs==0 ) + nencs = version; /* Sometimes they are backwards */ /* Or was I just confused early on? */ + cmap_encs = galloc(nencs*sizeof(struct cmap_encs)); + for ( i=usable_encs=0; i<nencs; ++i ) { + cmap_encs[usable_encs].platform = getushort(ttf); + cmap_encs[usable_encs].specific = getushort(ttf); + cmap_encs[usable_encs].offset = getlong(ttf); + if ( cmap_encs[usable_encs].platform == 0 && cmap_encs[usable_encs].specific == 5 ) { + /* This isn't a true encoding. */ + /* It's an optional set of encoding modifications (sort of) */ + /* applied to a format 4/10 encoding (unicode BMP/Full) */ + if ( SubtableMustBe14(ttf,info->encoding_start+cmap_encs[usable_encs].offset,info) ) + vs_map = info->encoding_start+cmap_encs[usable_encs].offset; + continue; + } + temp = enc_from_platspec(cmap_encs[usable_encs].platform,cmap_encs[usable_encs].specific); + if ( temp==NULL ) /* iconv doesn't support this. Some sun iconvs seem limited */ + temp = FindOrMakeEncoding("Custom"); + cmap_encs[usable_encs].enc = temp; + if ( SubtableIsntSupported(ttf,info->encoding_start+cmap_encs[usable_encs].offset, + &cmap_encs[usable_encs],info)) + continue; + ++usable_encs; + } + if ( usable_encs==0 ) { + LogError( _("Could not find any valid encoding tables" )); + free(cmap_encs); +return; + } + def = -1; + enc = &custom; + unicode_cmap = unicode4_cmap = -1; + for ( i=0; i<usable_encs; ++i ) { + temp = cmap_encs[i].enc; + platform = cmap_encs[i].platform; + specific = cmap_encs[i].specific; + offset = cmap_encs[i].offset; + + if ( (platform==3 && specific==10) || (platform==0 && specific==4) ) { /* MS Unicode 4 byte */ + enc = temp; + def = i; + unicode4_cmap = i; + } else if ( !enc->is_unicodefull && (!prefer_cjk_encodings || + (!enc->is_japanese && !enc->is_korean && !enc->is_tradchinese && + !enc->is_simplechinese)) && + (( platform==3 && specific==1 ) || /* MS Unicode */ +/* Well I should only deal with apple unicode specific==0 (default) and 3 (U2.0 semantics) */ +/* but apple ships dfonts with specific==1 (Unicode 1.1 semantics) */ +/* which is stupid of them */ + ( platform==0 /*&& (specific==0 || specific==3)*/ ))) { /* Apple Unicode */ + enc = temp; + def = i; + } else if ( platform==3 && specific==0 && enc->is_custom ) { + /* Only select symbol if we don't have something better */ + enc = temp; + def = i; + /* Now I had assumed this would be a 1 byte encoding, but it turns*/ + /* out to map into the unicode private use area at U+f000-U+F0FF */ + /* so it's a 2 byte enc */ +/* Mac platform specific encodings are script numbers. 0=>roman, 1=>jap, 2=>big5, 3=>korean, 4=>arab, 5=>hebrew, 6=>greek, 7=>cyrillic, ... 25=>simplified chinese */ + } else if ( platform==1 && specific==0 && enc->is_custom ) { + enc = temp; + def = i; + } else if ( platform==1 && (specific==2 ||specific==1||specific==3||specific==25) && + !enc->is_unicodefull && + (prefer_cjk_encodings || !enc->is_unicodebmp) ) { + enc = temp; + def = i; + } else if ( platform==3 && (specific>=2 && specific<=6 ) && + !enc->is_unicodefull && + (prefer_cjk_encodings || !enc->is_unicodebmp) ) { + /* Old ms docs say that specific==3 => big 5, new docs say specific==4 => big5 */ + /* Ain't that jus' great? */ + enc = temp; + def = i; + } + if ( (platform==3 && specific==1) || + (platform==0 && specific==3)) + unicode_cmap = i; + } + + if ( justinuse==git_justinuse || !ask_user_for_cmap || (i = PickCMap(cmap_encs,usable_encs,def))==-1 ) + i = def; + + if ( i==-1 ) { + LogError( _("Could not find a usable encoding table" )); + free(cmap_encs); +return; + } + + info->platform = cmap_encs[i].platform; + info->specific = cmap_encs[i].specific; + + desired_cmaps[0] = cmap_encs[i]; dcnt = 1; + if ( unicode4_cmap!=-1 ) { + if ( i!=unicode4_cmap ) { + desired_cmaps[1] = cmap_encs[unicode4_cmap]; + ++dcnt; + } + } else if ( unicode_cmap!=-1 ) { + if ( i!=unicode_cmap ) { + desired_cmaps[1] = cmap_encs[unicode_cmap]; + ++dcnt; + } + } else { + if ( i!=def && def!=-1 ) { + desired_cmaps[1] = cmap_encs[def]; + ++dcnt; + } + } + + map = NULL; + if ( justinuse==git_justinuse ) { + dcmap_cnt = usable_encs; + dcmap = cmap_encs; + } else { + dcmap_cnt = dcnt; + dcmap = desired_cmaps; + } + for ( dc=dcmap_cnt-1; dc>=0; --dc ) { + /* if justinuse then look at all cmaps and tick the glyphs they use */ + /* otherwise dcmap_cnt will be either 1 or 2. If 1 then this subtable */ + /* contains both the encoding and the source for unicode encodings */ + /* if dcmap_cnt==2 then when dc==0 we are setting up the encoding */ + /* and when dc==1 we are setting up the unicode code points */ + int dounicode = (dc==dcmap_cnt-1); + enc = dcmap[dc].enc; + encoff = dcmap[dc].offset; + + if ( dc==0 && justinuse==git_normal ) { + interp = interp_from_encoding(enc,ui_none); + mod = 0; + if ( dcmap[dc].platform==3 && (dcmap[dc].specific>=2 && dcmap[dc].specific<=6 )) + mod = dcmap[dc].specific; + else if ( dcmap[dc].platform==1 && (dcmap[dc].specific==2 ||dcmap[dc].specific==1||dcmap[dc].specific==3||dcmap[dc].specific==25)) + mod = dcmap[dc].specific==1?2:dcmap[dc].specific==2?4:dcmap[dc].specific==3?5:3; /* convert to ms specific */ + info->map = map = EncMapNew(enc->char_cnt,info->glyph_cnt,enc); + info->uni_interp = interp; + } + + fseek(ttf,info->encoding_start+encoff,SEEK_SET); + format = getushort(ttf); + if ( format!=12 && format!=10 && format!=8 ) { + len = getushort(ttf); + /* version/language = */ getushort(ttf); + } else { + /* padding */ getushort(ttf); + len = getlong(ttf); + /* language = */ getlong(ttf); + } + if ( enc->is_unicodebmp && (format==8 || format==10 || format==12)) + enc = FindOrMakeEncoding("UnicodeFull"); + + if ( format==0 ) { + if ( justinuse==git_normal && map!=NULL && map->enccount<256 ) { + map->map = grealloc(map->map,256*sizeof(int)); + memset(map->map,-1,(256-map->enccount)*sizeof(int)); + map->enccount = map->encmax = 256; + } + for ( i=0; i<len-6; ++i ) + table[i] = getc(ttf); + trans = enc->unicode; + if ( trans==NULL && dcmap[dc].platform==1 ) + trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1); + for ( i=0; i<256 && i<len-6; ++i ) + if ( justinuse==git_normal ) { + if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) { + if ( map!=NULL ) + map->map[i] = table[i]; + if ( dounicode && trans!=NULL ) + info->chars[table[i]]->unicodeenc = trans[i]; + } + } else if ( table[i]<info->glyph_cnt && info->chars[table[i]]!=NULL ) + info->inuse[table[i]] = 1; + } else if ( format==4 ) { + segCount = getushort(ttf)/2; + /* searchRange = */ getushort(ttf); + /* entrySelector = */ getushort(ttf); + /* rangeShift = */ getushort(ttf); + endchars = galloc(segCount*sizeof(uint16)); + used = gcalloc(65536,sizeof(uint8)); + for ( i=0; i<segCount; ++i ) + endchars[i] = getushort(ttf); + if ( getushort(ttf)!=0 ) + IError("Expected 0 in 'cmap' format 4 subtable"); + startchars = galloc(segCount*sizeof(uint16)); + for ( i=0; i<segCount; ++i ) + startchars[i] = getushort(ttf); + delta = galloc(segCount*sizeof(uint16)); + for ( i=0; i<segCount; ++i ) + delta[i] = getushort(ttf); + rangeOffset = galloc(segCount*sizeof(uint16)); + for ( i=0; i<segCount; ++i ) + rangeOffset[i] = getushort(ttf); + len -= 8*sizeof(uint16) + + 4*segCount*sizeof(uint16); + /* that's the amount of space left in the subtable and it must */ + /* be filled with glyphIDs */ + if ( len<0 ) { + IError("This font has an illegal format 4 subtable with too little space for all the segments.\nThis error is not recoverable.\nBye" ); + exit(1); + } + glyphs = galloc(len); + glyph_tot = len/2; + for ( i=0; i<glyph_tot; ++i ) + glyphs[i] = getushort(ttf); + for ( i=0; i<segCount; ++i ) { + if ( rangeOffset[i]==0 && startchars[i]==0xffff ) + /* Done */; + else if ( rangeOffset[i]==0 ) { + for ( j=startchars[i]; j<=endchars[i]; ++j ) { + if ( justinuse==git_justinuse && (uint16) (j+delta[i])<info->glyph_cnt ) + info->inuse[(uint16) (j+delta[i])] = true; + else if ( (uint16) (j+delta[i])>=info->glyph_cnt || info->chars[(uint16) (j+delta[i])]==NULL ) { + LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"), + (uint16) (j+delta[i]), modenc(j,mod), modenc(j,mod)); + info->bad_cmap = true; + } else { + int uenc = umodenc(j,mod,info); + int lenc = modenc(j,mod); + if ( uenc!=-1 && used[uenc] ) { + if ( !badencwarned ) { + LogError( _("Multiple glyphs map to the same unicode encoding U+%04X, only one will be used\n"), uenc ); + info->bad_cmap = true; + badencwarned = true; + } + } else { + if ( uenc!=-1 && dounicode ) used[uenc] = true; + if ( dounicode && info->chars[(uint16) (j+delta[i])]->unicodeenc==-1 ) + info->chars[(uint16) (j+delta[i])]->unicodeenc = uenc; + if ( map!=NULL && lenc<map->enccount ) + map->map[lenc] = (uint16) (j+delta[i]); + } + } + } + } else if ( rangeOffset[i]!=0xffff ) { + /* Apple says a rangeOffset of 0xffff means no glyph */ + /* OpenType doesn't mention this */ + for ( j=startchars[i]; j<=endchars[i]; ++j ) { + int temp = (i-segCount+rangeOffset[i]/2) + j-startchars[i]; + if ( temp<glyph_tot ) + index = glyphs[ temp ]; + else { + /* This happened in mingliu.ttc(PMingLiU) */ + if ( justinuse==git_normal ) { + LogError( _("Glyph index out of bounds. Was %d, must be less than %d.\n In attempt to associate a glyph with encoding %x in segment %d\n with platform=%d, specific=%d (in 'cmap')\n"), + temp, glyph_tot, j, i, dcmap[dc].platform, dcmap[dc].specific ); + info->bad_cmap = true; + } + index = 0; + } + if ( index!=0 ) { + index = (unsigned short) (index+delta[i]); + if ( index>=info->glyph_cnt ) { + /* This isn't mentioned either, but in some */ + /* MS Chinese fonts (kaiu.ttf) the index */ + /* goes out of bounds. and MS's ttf dump */ + /* program says it is treated as 0 */ + LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"), + index, modenc(j,mod), modenc(j,mod)); + info->bad_cmap = true; + } else if ( justinuse==git_justinuse ) + info->inuse[index] = 1; + else if ( info->chars[index]==NULL ) { + LogError( _("Attempt to encode missing glyph %d to %d (0x%x)\n"), + index, modenc(j,mod), modenc(j,mod)); + info->bad_cmap = true; + } else { + int uenc = umodenc(j,mod,info); + int lenc = modenc(j,mod); + if ( uenc!=-1 && used[uenc] ) { + if ( !badencwarned ) { + LogError( _("Multiple glyphs map to the same unicode encoding U+%04X, only one will be used\n"), uenc ); + info->bad_cmap = true; + badencwarned = true; + } + } else { + if ( uenc!=-1 && dounicode ) used[uenc] = true; + if ( dounicode && info->chars[index]->unicodeenc==-1 ) + info->chars[index]->unicodeenc = uenc; + if ( map!=NULL && lenc<map->enccount ) + map->map[lenc] = index; + } + } + } + } + } else { + LogError( _("Use of a range offset of 0xffff to mean a missing glyph in cmap table\n") ); + info->bad_cmap = true; + } + } + free(glyphs); + free(rangeOffset); + free(delta); + free(startchars); + free(endchars); + free(used); + } else if ( format==6 ) { + /* trimmed array format */ + /* Well, the docs say it's for 2byte encodings, but Apple actually*/ + /* uses it for 1 byte encodings which don't fit into the require-*/ + /* ments for a format 0 sub-table. See Zapfino.dfont */ + int first, count; + first = getushort(ttf); + count = getushort(ttf); + trans = enc->unicode; + if ( trans==NULL && dcmap[dc].platform==1 && first+count<=256 ) + trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1); + if ( justinuse==git_justinuse ) + for ( i=0; i<count; ++i ) + info->inuse[getushort(ttf)]= 1; + else { + for ( i=0; i<count; ++i ) { + int gid = getushort(ttf); + if ( dounicode ) + info->chars[gid]->unicodeenc = trans!=NULL ? trans[first+1] : first+i; + if ( map!=NULL && first+i < map->enccount ) + map->map[first+i] = gid; + } + } + } else if ( format==2 ) { + int max_sub_head_key = 0, cnt, max_pos= -1; + struct subhead *subheads; + + for ( i=0; i<256; ++i ) { + table[i] = getushort(ttf)/8; /* Sub-header keys */ + if ( table[i]>max_sub_head_key ) { + max_sub_head_key = table[i]; /* The entry is a byte pointer, I want a pointer in units of struct subheader */ + max_pos = i; + } + } + subheads = galloc((max_sub_head_key+1)*sizeof(struct subhead)); + for ( i=0; i<=max_sub_head_key; ++i ) { + subheads[i].first = getushort(ttf); + subheads[i].cnt = getushort(ttf); + subheads[i].delta = getushort(ttf); + subheads[i].rangeoff = (getushort(ttf)- + (max_sub_head_key-i)*sizeof(struct subhead)- + sizeof(short))/sizeof(short); + } + cnt = (len-(ftell(ttf)-(info->encoding_start+encoff)))/sizeof(short); + /* The count is the number of glyph indexes to read. it is the */ + /* length of the entire subtable minus that bit we've read so far */ + glyphs = galloc(cnt*sizeof(short)); + for ( i=0; i<cnt; ++i ) + glyphs[i] = getushort(ttf); + last = -1; + for ( i=0; i<256; ++i ) { + if ( table[i]==0 ) { + /* Special case, single byte encoding entry, look it up in */ + /* subhead */ + /* In the one example I've got of this encoding (wcl-02.ttf) the chars */ + /* 0xfd, 0xfe, 0xff are said to exist but there is no mapping */ + /* for them. */ + if ( i>=max_pos ) + index = 0; /* the subhead says there are 256 entries, but in fact there are only 193, so attempting to find these guys should give an error */ + else if ( i<subheads[0].first || i>=subheads[0].first+subheads[0].cnt || + subheads[0].rangeoff+(i-subheads[0].first)>=cnt ) + index = 0; + else if ( (index = glyphs[subheads[0].rangeoff+(i-subheads[0].first)])!= 0 ) + index = (uint32) (index+subheads[0].delta); + /* I assume the single byte codes are just ascii or latin1*/ + if ( index!=0 && index<info->glyph_cnt ) { + if ( justinuse==git_justinuse ) + info->inuse[index] = 1; + else if ( info->chars[index]==NULL ) + /* Do Nothing */; + else { + int lenc = modenc(i,mod); + if ( dounicode && info->chars[index]->unicodeenc==-1 ) + info->chars[index]->unicodeenc = i; + if ( map!=NULL && lenc<map->enccount ) + map->map[lenc] = index; + } + } + } else { + int k = table[i]; + for ( j=0; j<subheads[k].cnt; ++j ) { + int enc, lenc; + if ( subheads[k].rangeoff+j>=cnt ) + index = 0; + else if ( (index = glyphs[subheads[k].rangeoff+j])!= 0 ) + index = (uint16) (index+subheads[k].delta); + if ( index!=0 && index<info->glyph_cnt ) { + enc = (i<<8)|(j+subheads[k].first); + lenc = modenc(enc,mod); + if ( justinuse==git_justinuse ) + info->inuse[index] = 1; + else if ( info->chars[index]==NULL ) + /* Do Nothing */; + else { + if ( dounicode && info->chars[index]->unicodeenc==-1 ) + info->chars[index]->unicodeenc = umodenc(enc,mod,info); + if ( map!=NULL && lenc<map->enccount ) + map->map[lenc] = index; + } + } + } + /*if ( last==-1 ) last = i;*/ + } + } + free(subheads); + free(glyphs); + } else if ( format==8 ) { + uint32 ngroups, start, end, startglyph; + if ( !enc->is_unicodefull ) { + IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" ); + enc = FindOrMakeEncoding("UnicodeFull"); + } + /* I'm now assuming unicode surrogate encoding, so I just ignore */ + /* the is32 table (it will be set for the surrogates and not for */ + /* anything else */ + fseek(ttf,8192,SEEK_CUR); + ngroups = getlong(ttf); + for ( j=0; j<(int)ngroups; ++j ) { + start = getlong(ttf); + end = getlong(ttf); + startglyph = getlong(ttf); + if ( justinuse==git_justinuse ) + for ( i=start; i<=(int)end; ++i ) + info->inuse[startglyph+i-start]= 1; + else + for ( i=start; i<=(int)end; ++i ) { + int uenc = ((i>>16)-0xd800)*0x400 + (i&0xffff)-0xdc00 + 0x10000; + sc = info->chars[startglyph+i-start]; + if ( dounicode && sc->unicodeenc==-1 ) + sc->unicodeenc = uenc; + if ( map!=NULL && sc->unicodeenc < map->enccount ) + map->map[uenc] = startglyph+i-start; + } + } + } else if ( format==10 ) { + /* same as format 6, except for 4byte chars */ + int first, count; + if ( !enc->is_unicodefull ) { + IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" ); + enc = FindOrMakeEncoding("UnicodeFull"); + } + first = getlong(ttf); + count = getlong(ttf); + if ( justinuse==git_justinuse ) + for ( i=0; i<count; ++i ) + info->inuse[getushort(ttf)]= 1; + else + for ( i=0; i<count; ++i ) { + int gid = getushort(ttf); + if ( dounicode ) + info->chars[gid]->unicodeenc = first+i; + if ( map!=NULL && first+i < map->enccount ) + map->map[first+i] = gid; + } + } else if ( format==12 ) { + uint32 ngroups, start, end, startglyph; + if ( !enc->is_unicodefull ) { + IError("I don't support 32 bit characters except for the UCS-4 (MS platform, specific=10)" ); + enc = FindOrMakeEncoding("UnicodeFull"); + } + ngroups = getlong(ttf); + for ( j=0; j<(int)ngroups; ++j ) { + start = getlong(ttf); + end = getlong(ttf); + startglyph = getlong(ttf); + if ( justinuse==git_justinuse ) { + for ( i=start; i<=(int)end; ++i ) + if ( startglyph+i-start < (unsigned)info->glyph_cnt ) + info->inuse[startglyph+i-start]= 1; + else + break; + } else + for ( i=start; i<=(int)end; ++i ) { + if ( startglyph+i-start >= (unsigned)info->glyph_cnt || + info->chars[startglyph+i-start]==NULL ) { + LogError( _("Bad font: Encoding data out of range.\n") ); + info->bad_cmap = true; + break; + } else { + if ( dounicode ) + info->chars[startglyph+i-start]->unicodeenc = i; + if ( map!=NULL && i < map->enccount ) + map->map[i] = startglyph+i-start; + } + } + } + } + } + free(cmap_encs); + if ( info->chars!=NULL ) + for ( i=0; i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL && info->chars[i]->unicodeenc==0xffff ) + info->chars[i]->unicodeenc = -1; + info->vs_start = vs_map; + if ( vs_map!=0 ) + ApplyVariationSequenceSubtable(ttf,vs_map,info,justinuse); + if ( justinuse==git_normal ) { + if ( interp==ui_none ) + info->uni_interp = amscheck(info,map); + map->enc = enc; /* This can be changed from the initial value */ + } + info->map = map; +} + +static void readttfos2metrics(FILE *ttf,struct ttfinfo *info) { + int i, sel; + + fseek(ttf,info->os2_start,SEEK_SET); + info->os2_version = getushort(ttf); +#ifdef LUA_FF_LIB + info->pfminfo.avgwidth = getushort(ttf); +#else + /* avgwidth */ getushort(ttf); +#endif + info->pfminfo.weight = getushort(ttf); + info->pfminfo.width = getushort(ttf); + info->pfminfo.fstype = getushort(ttf); + info->pfminfo.os2_subxsize = getushort(ttf); + info->pfminfo.os2_subysize = getushort(ttf); + info->pfminfo.os2_subxoff = getushort(ttf); + info->pfminfo.os2_subyoff = getushort(ttf); + info->pfminfo.os2_supxsize = getushort(ttf); + info->pfminfo.os2_supysize = getushort(ttf); + info->pfminfo.os2_supxoff = getushort(ttf); + info->pfminfo.os2_supyoff = getushort(ttf); + info->pfminfo.os2_strikeysize = getushort(ttf); + info->pfminfo.os2_strikeypos = getushort(ttf); + info->pfminfo.os2_family_class = getushort(ttf); + for ( i=0; i<10; ++i ) + info->pfminfo.panose[i] = getc(ttf); + info->pfminfo.pfmfamily = info->pfminfo.panose[0]==2 ? 0x11 : /* might be 0x21 */ /* Text & Display maps to either serif 0x11 or sans 0x21 or monospace 0x31 */ + info->pfminfo.panose[0]==3 ? 0x41 : /* Script */ + info->pfminfo.panose[0]==4 ? 0x51 : /* Decorative */ + 0x51; /* And pictorial doesn't fit into pfm */ + info->pfminfo.unicoderanges[0] = getlong(ttf); + info->pfminfo.unicoderanges[1] = getlong(ttf); + info->pfminfo.unicoderanges[2] = getlong(ttf); + info->pfminfo.unicoderanges[3] = getlong(ttf); + info->pfminfo.hasunicoderanges = true; + info->pfminfo.os2_vendor[0] = getc(ttf); + info->pfminfo.os2_vendor[1] = getc(ttf); + info->pfminfo.os2_vendor[2] = getc(ttf); + info->pfminfo.os2_vendor[3] = getc(ttf); + sel = getushort(ttf); + if ( info->os2_version>=4 ) { + info->use_typo_metrics = (sel&128)?1:0; + info->weight_width_slope_only = (sel&256)?1:0; + } +#ifdef LUA_FF_LIB + info->pfminfo.firstchar = getushort(ttf); + info->pfminfo.lastchar = getushort(ttf); +#else + /* firstchar */ getushort(ttf); + /* lastchar */ getushort(ttf); +#endif + info->pfminfo.os2_typoascent = getushort(ttf); + info->pfminfo.os2_typodescent = (short) getushort(ttf); + if ( info->pfminfo.os2_typoascent-info->pfminfo.os2_typodescent == info->emsize ) { + info->ascent = info->pfminfo.os2_typoascent; + info->descent = -info->pfminfo.os2_typodescent; + } + info->pfminfo.os2_typolinegap = getushort(ttf); + info->pfminfo.os2_winascent = getushort(ttf); + info->pfminfo.os2_windescent = getushort(ttf); + info->pfminfo.winascent_add = info->pfminfo.windescent_add = false; + info->pfminfo.typoascent_add = info->pfminfo.typodescent_add = false; + info->pfminfo.pfmset = true; + info->pfminfo.panose_set = true; + info->pfminfo.subsuper_set = true; + if ( info->os2_version>=1 ) { + info->pfminfo.codepages[0] = getlong(ttf); + info->pfminfo.codepages[1] = getlong(ttf); + info->pfminfo.hascodepages = true; + } +#ifdef LUA_FF_LIB + if ( info->os2_version>=3 ) { /* TH just in case */ + info->pfminfo.os2_xheight = getushort(ttf); /* four new fields */ + info->pfminfo.os2_capheight = getushort(ttf); + info->pfminfo.os2_defaultchar = getushort(ttf); + info->pfminfo.os2_breakchar = getushort(ttf); + } +#endif + if ( info->os2_version==0 ) { + LogError("Windows will reject fonts with an OS/2 version number of 0\n"); + info->bad_os2_version = true; + } else if ( info->os2_version==1 && info->cff_start!=0 ) { + LogError("Windows will reject otf (cff) fonts with an OS/2 version number of 1\n"); + info->bad_os2_version = true; + } +} + +#if 0 +static int cmapEncFromName(struct ttfinfo *info,const char *nm, int glyphid) { + int uni; + int i; + + if ( info->map!=NULL ) + uni = EncFromName(nm,info->uni_interp,info->map->enc); + else + uni = EncFromName(nm,ui_none,&custom); + if ( uni==-1 ) +return( -1 ); + + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) { + if ( info->chars[i]->unicodeenc==uni ) { + if ( info->complainedmultname ) + /* Don't do it again */; + else if ( info->chars[i]->name!=NULL && strcmp(info->chars[i]->name,nm)==0 ) + LogError( _("Warning: Glyph %d has the same name (%s) as Glyph %d\n"), + i, nm, glyphid ); + else + LogError( _("Warning: Glyph %d is named %s which should mean it is mapped to\n Unicode U+%04X, but Glyph %d already has that encoding.\n"), + glyphid, nm, uni, i); + info->complainedmultname = true; +return( -1 ); + } + } +return( uni ); +} +#endif + +static void readttfpostnames(FILE *ttf,struct ttfinfo *info) { + int i,j; + int format, len, gc, gcbig, val; + const char *name; + char buffer[30]; + uint16 *indexes; + extern const char *ttfstandardnames[]; + int notdefwarned = false; + int anynames = false; + + ff_progress_change_line2(_("Reading Names")); + + /* Give ourselves an xuid, just in case they want to convert to PostScript*/ + /* (even type42) */ + if ( xuid!=NULL && info->fd==NULL && info->xuid==NULL ) { + info->xuid = galloc(strlen(xuid)+20); + sprintf(info->xuid,"[%s %d]", xuid, (rand()&0xffffff)); + } + + if ( info->postscript_start!=0 ) { + fseek(ttf,info->postscript_start,SEEK_SET); + format = getlong(ttf); + info->italicAngle = getfixed(ttf); + info->upos = (short) getushort(ttf); + info->uwidth = (short) getushort(ttf); + info->upos += info->uwidth/2; /* 'post' defn of this field is different from FontInfo defn and I didn't notice */ + /* fixedpitch = */ getlong(ttf); + /* mem1 = */ getlong(ttf); + /* mem2 = */ getlong(ttf); + /* mem3 = */ getlong(ttf); + /* mem4 = */ getlong(ttf); + if ( format==0x00020000 ) { + gc = getushort(ttf); + indexes = gcalloc(65536,sizeof(uint16)); + /* the index table is backwards from the way I want to use it */ + gcbig = 0; + for ( i=0; i<gc; ++i ) { + indexes[val = getushort(ttf)] = i; + if ( val>=258 ) ++gcbig; + } + + /* if we are only loading bitmaps, we can get holes in our data */ + for ( i=0; i<258; ++i ) if ( indexes[i]!=0 || i==0 ) if ( indexes[i]<info->glyph_cnt && info->chars[indexes[i]]!=NULL ) { + info->chars[indexes[i]]->name = copy(ttfstandardnames[i]); +#if 0 /* Too many fonts have badly named glyphs */ + if ( info->chars[indexes[i]]->unicodeenc==-1 ) + info->chars[indexes[i]]->unicodeenc = cmapEncFromName(info,ttfstandardnames[i],indexes[i]); +#endif + } + gcbig += 258; + for ( i=258; i<gcbig; ++i ) { + char *nm; + len = getc(ttf); + if ( len<0 ) /* Don't crash on EOF */ + break; + nm = galloc(len+1); + for ( j=0; j<len; ++j ) + nm[j] = getc(ttf); + nm[j] = '\0'; + if ( indexes[i]<info->glyph_cnt && info->chars[indexes[i]]!=NULL ) { + info->chars[indexes[i]]->name = nm; +#if 0 /* Too many fonts have badly named glyphs */ + if ( info->chars[indexes[i]]->unicodeenc==-1 ) + info->chars[indexes[i]]->unicodeenc = cmapEncFromName(info,nm,indexes[i]); +#endif + } + } + free(indexes); + anynames = true; + } + } + + if ( info->fd!=NULL && info->fd->chars!=NULL) { + EncMap *map = NULL; + struct pschars *chars = info->fd->chars; + if ( info->map==NULL ) + info->map = map = EncMapNew(65536,65536,FindOrMakeEncoding("UnicodeBmp")); + /* In type42 fonts the names are stored in a postscript /CharStrings dictionary */ + for ( i=0; i<chars->next; ++i ) { + int gid = (intpt) (chars->values[i]); + if ( gid>=0 && gid<info->glyph_cnt && chars->keys[i]!=NULL ) { + free(info->chars[gid]->name); + info->chars[gid]->name = chars->keys[i]; + info->chars[gid]->unicodeenc = UniFromName(chars->keys[i],info->uni_interp,info->map->enc); + if ( map!=NULL && info->chars[gid]->unicodeenc!=-1 && + info->chars[gid]->unicodeenc<map->enccount) + map->map[ info->chars[gid]->unicodeenc ] = gid; + chars->keys[i] = NULL; + chars->values[i] = NULL; + } else + chars->values[i] = NULL; + } + } + + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) { + /* info->chars[i] can be null in some TTC files */ + if ( i!=0 && info->chars[i]->name!=NULL && + strcmp(info->chars[i]->name,".notdef")==0 ) { + /* for some reason MS puts out fonts where several characters */ + /* are called .notdef (and only one is a real notdef). So if we */ + /* find a glyph other than 0 called ".notdef" then pretend it had */ + /* no name */ + if ( !notdefwarned ) { + notdefwarned = true; + LogError( _("Glyph %d is called \".notdef\", a singularly inept choice of name (only glyph 0\n may be called .notdef)\nFontForge will rename it.\n"), i ); + } + free(info->chars[i]->name); + info->chars[i]->name = NULL; + /* I used to check for glyphs with bad names (ie. names indicative of */ + /* another unicode code point than the one applied to the glyph) but */ + /* this proves too early for that check, as we don't have the altunis*/ + /* figured out yet. So I've moved that into its own routine later */ + } + /* And some volt files actually assign nul strings to the name */ + if ( (info->chars[i]->name!=NULL && *info->chars[i]->name!='\0' )) + continue; + free(info->chars[i]->name); /* If it's a null string get rid of it */ + if ( i==0 ) + name = ".notdef"; + else if ( info->chars[i]->unicodeenc==-1 ) { + /* Do this later */; + name = NULL; + } else { + name = StdGlyphName(buffer,info->chars[i]->unicodeenc,info->uni_interp,NULL); + if ( anynames ) { + for ( j=0; j<info->glyph_cnt; ++j ) { + if ( info->chars[j]!=NULL && j!=i && info->chars[j]->name!=NULL ) { + if ( strcmp(info->chars[j]->name,name)==0 ) { + name = NULL; + break; + } + } + } + } + } + ff_progress_next(); + info->chars[i]->name = copy(name); + } + + /* If we have a GSUB table we can give some unencoded glyphs names */ + /* for example if we have a vrt2 substitution of A to <unencoded> */ + /* we could name the unencoded "A.vrt2" (though in this case we might */ + /* try A.vert instead */ /* Werner suggested this */ + /* We could try this from morx too, except that apple features don't */ + /* use meaningful ids. That is A.15,3 isn't very readable */ + for ( i=info->glyph_cnt-1; i>=0 ; --i ) + if ( info->chars[i]!=NULL && info->chars[i]->name==NULL ) + break; + if ( i>=0 && info->vs_start!=0 ) + ApplyVariationSequenceSubtable(ttf,info->vs_start,info,git_findnames); + if ( i>=0 && info->gsub_start!=0 ) + GuessNamesFromGSUB(ttf,info); + if ( i>=0 && info->math_start!=0 ) + GuessNamesFromMATH(ttf,info); + + for ( i=0; i<info->glyph_cnt; ++i ) { + /* info->chars[i] can be null in some TTC files */ + if ( info->chars[i]==NULL ) + continue; + if ( info->chars[i]->name!=NULL ) + continue; + if ( info->ordering!=NULL ) + sprintf(buffer, "%.20s-%d", info->ordering, i ); + else if ( info->map!=NULL && info->map->backmap[i]!=-1 ) + sprintf(buffer, "nounicode.%d.%d.%x", info->platform, info->specific, + (int) info->map->backmap[i] ); + else + sprintf( buffer, "glyph%d", i ); + info->chars[i]->name = copy(buffer); + ff_progress_next(); + } + ff_progress_next_stage(); +} + +static void readttfgasp(FILE *ttf,struct ttfinfo *info) { + int i, cnt; + + if ( info->gasp_start==0 ) +return; + + fseek(ttf,info->gasp_start,SEEK_SET); + info->gasp_version = getushort(ttf); + if ( info->gasp_version!=0 && info->gasp_version!=1 ) +return; /* We only support 'gasp' versions 0&1 (no other versions currently) */ + info->gasp_cnt = cnt = getushort(ttf); + if ( cnt==0 ) +return; + info->gasp = galloc(cnt*sizeof(struct gasp)); + for ( i=0; i<cnt; ++i ) { + info->gasp[i].ppem = getushort(ttf); + info->gasp[i].flags = getushort(ttf); + } +} + +static void UnfigureControls(Spline *spline,BasePoint *pos) { + pos->x = rint( (spline->splines[0].c+2*spline->splines[0].d)/2 ); + pos->y = rint( (spline->splines[1].c+2*spline->splines[1].d)/2 ); +} + +int ttfFindPointInSC(SplineChar *sc,int layer,int pnum,BasePoint *pos, + RefChar *bound) { + SplineSet *ss; + SplinePoint *sp; + int last=0, ret; + RefChar *refs; + + for ( ss = sc->layers[layer].splines; ss!=NULL; ss=ss->next ) { + for ( sp=ss->first; ; ) { + if ( sp->ttfindex==pnum ) { + *pos = sp->me; +return(-1); + } else if ( sp->nextcpindex==pnum ) { + if ( sp->next!=NULL && sp->next->order2 ) + *pos = sp->nextcp; + else { + /* fix this up to be 2 degree bezier control point */ + UnfigureControls(sp->next,pos); + } +return( -1 ); + } + if ( !sp->nonextcp && last<=sp->nextcpindex ) + last = sp->nextcpindex+1; + else if ( sp->ttfindex!=0xffff ) + last = sp->ttfindex+1; + if ( sp->next==NULL ) + break; + sp = sp->next->to; + if ( sp==ss->first ) + break; + } + } + for ( refs=sc->layers[layer].refs; refs!=NULL; refs=refs->next ) { + if ( refs==bound ) { + LogError( _("Invalid point match. Point would be after this reference.\n") ); +return( 0x800000 ); + } + ret = ttfFindPointInSC(refs->sc,ly_fore,pnum-last,pos,NULL); + if ( ret==-1 ) { + BasePoint p; + p.x = refs->transform[0]*pos->x + refs->transform[2]*pos->y + refs->transform[4]; + p.y = refs->transform[1]*pos->x + refs->transform[3]*pos->y + refs->transform[5]; + *pos = p; +return( -1 ); + } + last += ret; + if ( last>pnum ) { + IError("Point match failure last=%d, pnum=%d", last, pnum ); +return( 0x800000 ); + } + } +return( last ); /* Count of number of points in the character */ +} + +static void ttfPointMatch(SplineChar *sc,RefChar *rf) { + BasePoint sofar, inref; + + if ( ttfFindPointInSC(sc,ly_fore,rf->match_pt_base,&sofar,rf)!=-1 || + ttfFindPointInSC(rf->sc,ly_fore,rf->match_pt_ref,&inref,NULL)!=-1 ) { + LogError( _("Could not match points in composite glyph (%d to %d) when adding %s to %s\n"), + rf->match_pt_base, rf->match_pt_ref, rf->sc->name, sc->name); +return; + } + rf->transform[4] = sofar.x-inref.x; + rf->transform[5] = sofar.y-inref.y; +} + +int ttfFixupRef(SplineChar **chars,int i) { + RefChar *ref, *prev, *next; + + if ( chars[i]==NULL ) /* Can happen in ttc files */ +return( false ); + if ( chars[i]->ticked ) +return( false ); + chars[i]->ticked = true; + prev = NULL; + for ( ref=chars[i]->layers[ly_fore].refs; ref!=NULL; ref=next ) { + if ( ref->sc!=NULL ) + break; /* Already done */ + next = ref->next; + if ( !ttfFixupRef(chars,ref->orig_pos)) { + if ( prev==NULL ) + chars[i]->layers[ly_fore].refs = next; + else + prev->next = next; + chunkfree(ref,sizeof(RefChar)); + } else { + ref->sc = chars[ref->orig_pos]; + ref->adobe_enc = getAdobeEnc(ref->sc->name); + if ( ref->point_match ) + ttfPointMatch(chars[i],ref); + SCReinstanciateRefChar(chars[i],ref,ly_fore); + SCMakeDependent(chars[i],ref->sc); + prev = ref; + } + } + chars[i]->ticked = false; +return( true ); +} + +static void ttfFixupReferences(struct ttfinfo *info) { + int i; + + ff_progress_change_line2(_("Fixing up References")); + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) + info->chars[i]->ticked = false; + for ( i=0; i<info->glyph_cnt; ++i ) { + ttfFixupRef(info->chars,i); + ff_progress_next(); + } + ff_progress_next_stage(); +} + +static void TtfCopyTableBlindly(struct ttfinfo *info,FILE *ttf, + uint32 start,uint32 len,uint32 tag) { + struct ttf_table *tab; + + if ( start==0 || len==0 ) +return; + if ( len>0x1000000 ) { + LogError( _("Unlikely length for table, so I'm ignoring it. %u\n"), len ); +return; + } + + tab = chunkalloc(sizeof(struct ttf_table)); + tab->tag = tag; + tab->len = len; + tab->data = galloc(len); + fseek(ttf,start,SEEK_SET); + fread(tab->data,1,len,ttf); + tab->next = info->tabs; + info->tabs = tab; +} + +static int LookupListHasFeature(OTLookup *otl,uint32 tag) { + FeatureScriptLangList *feat; + + while ( otl!=NULL ) { + for ( feat = otl->features; feat!=NULL; feat=feat->next ) + if ( feat->featuretag == tag ) +return( true ); + otl = otl->next; + } +return( false ); +} + +static int readttf(FILE *ttf, struct ttfinfo *info, char *filename) { + char *oldloc; + int i; + + ff_progress_change_stages(3); + if ( !readttfheader(ttf,info,filename,&info->chosenname)) { +return( 0 ); + } + oldloc = setlocale(LC_NUMERIC,"C"); /* TrueType doesn't need this but opentype dictionaries do */ + readttfpreglyph(ttf,info); + ff_progress_change_total(info->glyph_cnt); + + /* If font only contains bitmaps, then only read bitmaps */ + if ( (info->glyphlocations_start==0 || info->glyph_length==0) && + info->cff_start==0 && info->typ1_start==0 && + info->bitmapdata_start!=0 && info->bitmaploc_start!=0 ) + info->onlystrikes = true; + + if ( !info->onlystrikes && + info->glyphlocations_start!=0 && info->glyph_start!=0 && + info->cff_start!=0 ) { + char *buts[4]; + int choice; + buts[0] = _("TTF 'glyf'"); + buts[1] = _("OTF 'CFF '"); + buts[2] = _("_Cancel"); + buts[3] = NULL; + choice = ff_ask(_("Pick a font, any font..."),(const char **) buts,0,2,_("This font contains both a TrueType 'glyf' table and an OpenType 'CFF ' table. FontForge can only deal with one at a time, please pick which one you want to use")); + if ( choice==2 ) { + setlocale(LC_NUMERIC,oldloc); +return( 0 ); + } else if ( choice==0 ) + info->cff_start=0; + else + info->glyph_start = info->glyphlocations_start = 0; + } + + if ( info->onlystrikes ) { + info->chars = gcalloc(info->glyph_cnt+1,sizeof(SplineChar *)); + info->to_order2 = new_fonts_are_order2; + } else if ( info->glyphlocations_start!=0 && info->glyph_start!=0 ) { + info->to_order2 = (!loaded_fonts_same_as_new || + (loaded_fonts_same_as_new && new_fonts_are_order2)); + /* If it's an apple mm font, then we don't want to change the order */ + /* This messes up the point count */ + if ( info->gvar_start!=0 && info->fvar_start!=0 ) + info->to_order2 = true; + readttfglyphs(ttf,info); + } else if ( info->cff_start!=0 ) { + info->to_order2 = (loaded_fonts_same_as_new && new_fonts_are_order2); + if ( !readcffglyphs(ttf,info) ) { + setlocale(LC_NUMERIC,oldloc); +return( 0 ); + } + } else if ( info->typ1_start!=0 ) { + if ( !readtyp1glyphs(ttf,info) ) { + setlocale(LC_NUMERIC,oldloc); +return( 0 ); + } + } else { + setlocale(LC_NUMERIC,oldloc); +return( 0 ); + } + if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 ) + ; /*TTFLoadBitmaps(ttf,info,info->onlyonestrike);*/ + else if ( info->onlystrikes ) + ff_post_error( _("No Bitmap Strikes"), _("No (useable) bitmap strikes in this TTF font: %s"), filename==NULL ? "<unknown>" : filename ); + if ( info->onlystrikes && info->bitmaps==NULL ) { + free(info->chars); + setlocale(LC_NUMERIC,oldloc); +return( 0 ); + } + if ( info->hmetrics_start!=0 ) + readttfwidths(ttf,info); + else if ( info->bitmapdata_start!=0 && info->bitmaploc_start!=0 ) + dummywidthsfromstrike(ttf,info); + if ( info->vmetrics_start!=0 && info->vhea_start!=0 ) + readttfvwidths(ttf,info); + /* 'cmap' is not meaningful for cid keyed fonts, and not supplied for */ + /* type42 fonts */ + if ( info->cidregistry==NULL && info->encoding_start!=0 ) + readttfencodings(ttf,info,git_normal); + if ( info->os2_start!=0 ) + readttfos2metrics(ttf,info); + readttfpostnames(ttf,info); /* If no postscript table we'll guess at names */ + if ( info->gdef_start!=0 ) /* ligature caret positioning info */ + readttfgdef(ttf,info); + else { + if ( info->prop_start!=0 ) + readttfprop(ttf,info); + if ( info->lcar_start!=0 ) + readttflcar(ttf,info); + } + if ( info->base_start!=0 ) + readttfbase(ttf,info); + else if ( info->bsln_start!=0 ) + readttfbsln(ttf,info); + if ( info->gasp_start!=0 ) + readttfgasp(ttf,info); + /* read the cvt table before reading variation data */ + if ( info->to_order2 ) { + /* Yes, even though we've looked at maxp already, let's make a blind */ + /* copy too for those fields we can't compute on our own */ + /* Like size of twilight zone, etc. */ + TtfCopyTableBlindly(info,ttf,info->maxp_start,info->maxp_len,CHR('m','a','x','p')); + TtfCopyTableBlindly(info,ttf,info->cvt_start,info->cvt_len,CHR('c','v','t',' ')); + TtfCopyTableBlindly(info,ttf,info->fpgm_start,info->fpgm_len,CHR('f','p','g','m')); + TtfCopyTableBlindly(info,ttf,info->prep_start,info->prep_len,CHR('p','r','e','p')); + } + for ( i=0; i<info->savecnt; ++i ) if ( info->savetab[i].offset!=0 ) + TtfCopyTableBlindly(info,ttf,info->savetab[i].offset,info->savetab[i].len,info->savetab[i].tag); + /* Do this before reading kerning info */ + if ( info->to_order2 && info->gvar_start!=0 && info->fvar_start!=0 ) + readttfvariations(info,ttf); + if ( info->gpos_start!=0 ) /* kerning info may live in the gpos table too */ + readttfgpossub(ttf,info,true); + /* Load the 'kern' table if the GPOS table either didn't exist or didn't */ + /* contain any kerning info */ + if ( info->kern_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('k','e','r','n'))) + readttfkerns(ttf,info); + if ( info->opbd_start!=0 && !LookupListHasFeature(info->gpos_lookups,CHR('l','f','b','d'))) + readttfopbd(ttf,info); + if ( info->gsub_start!=0 ) + readttfgpossub(ttf,info,false); + if ( info->morx_start!=0 || info->mort_start!=0 ) + readttfmort(ttf,info); + + if ( info->pfed_start!=0 ) + pfed_read(ttf,info); + if ( info->tex_start!=0 ) + tex_read(ttf,info); + if ( info->math_start!=0 ) + otf_read_math(ttf,info); + setlocale(LC_NUMERIC,oldloc); + if ( !info->onlystrikes && info->glyphlocations_start!=0 && info->glyph_start!=0 ) + ttfFixupReferences(info); + /* Can't fix up any postscript references until we create a SplineFont */ + /* so the check for cff is delayed. Generally there aren't any cff refs */ + /* anyway */ +return( true ); +} + +static void SymbolFixup(struct ttfinfo *info) { + /* convert a two-byte symbol encoding (one using PUA) into expected */ + /* one-byte encoding. */ + int i, max; + EncMap *map = info->map; + + max = -1; + for ( i=map->enccount-1; i>=0; --i ) { + if ( map->map[i]==-1 ) + continue; + if ( i>=0xf000 && i<=0xf0ff ) { + map->map[i-0xf000] = map->map[i]; + map->map[i] = -1; + continue; + } + if ( i>max ) max = i; + } + map->enccount = max; +} + +void AltUniFigure(SplineFont *sf,EncMap *map) { + int i,gid; + + if ( map->enc!=&custom ) { + for ( i=0; i<map->enccount; ++i ) if ( (gid = map->map[i])!=-1 ) { + int uni = UniFromEnc(i,map->enc); + AltUniAdd(sf->glyphs[gid],uni); + } + } +} + +static void NameConsistancyCheck(SplineFont *sf,EncMap *map) { + /* Many fonts seem to have glyph names which mean something other than */ + /* what the encoding says of the glyph */ + /* I used to ask about fixing the names up, but people didn't like that */ + /* so now I just produce warnings */ + int gid, uni; + SplineChar *sc; +#if 0 + char buffer[100]; + int response, asked=-1; + char *buts[5]; +#endif + +#if 0 + buts[0] = _("Yes"); buts[1] = _("Yes to _All"); + buts[2] = _("No _to All"); buts[3] = _("No"); + buts[4] = NULL; +#endif + + for ( gid = 0 ; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) { + if ( sc->name!=NULL && + strcmp(sc->name,".null")!=0 && + strcmp(sc->name,"nonmarkingreturn")!=0 && + (uni = UniFromName(sc->name,sf->uni_interp,map==NULL ? &custom : map->enc))!= -1 && + sc->unicodeenc != uni ) { +#if 1 + if ( uni>=0xe000 && uni<=0xf8ff ) + /* Don't complain about adobe's old PUA assignments for things like "eight.oldstyle" */; + else if ( uni<0x20 ) + /* Nor about control characters */; + else if ( sc->unicodeenc==-1 ) { +#if 0 + LogError(_("The glyph named %.30s is not mapped to any unicode code point.\nBut its name indicates it should be mapped to U+%04X.\n"), + sc->name,uni); +#endif + } else { + /* Ah, but suppose there's an altuni? */ + struct altuni *alt; + for ( alt = sc->altuni; alt!=NULL && alt->unienc!=uni; alt=alt->next ); + if ( alt==NULL ) + LogError( _("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\n"), + sc->name,sc->unicodeenc, uni); + else if ( alt->vs==0 ) { + alt->unienc = sc->unicodeenc; + sc->unicodeenc = uni; + } + } +#else + if ( asked!=-1 ) + response = asked; + else if ( sc->unicodeenc==-1 ) + response = ff_ask(_("Bad glyph name"),(const char **) buts,1,1,_("The glyph named %.30s is not mapped to any unicode code point. But its name indicates it should be mapped to U+%04X.\nWould you like to retain the name in spite of this?"), + sc->name,uni); + else + response = ff_ask(_("Bad glyph name"),(const char **) buts,1,1,_("The glyph named %.30s is mapped to U+%04X.\nBut its name indicates it should be mapped to U+%04X.\nWould you like to retain the name in spite of this?"), + sc->name,sc->unicodeenc, uni); + if ( response==1 ) + asked = response = 0; + else if ( response==2 ) + asked = response = 3; + if ( response==3 ) { + free(sc->name); + if ( sc->unicodeenc==-1 ) + sc->name = StdGlyphName(buffer,sc->unicodeenc,sf->uni_interp,NULL); + else { + sprintf( buffer, "glyph%d", gid ); + sc->name = copy( buffer ); + } + } +#endif + } + } +} + +static void UseGivenEncoding(SplineFont *sf,struct ttfinfo *info) { + int i; + RefChar *rf, *prev, *next; + SplineChar *sc; + + sf->glyphs = info->chars; + sf->glyphcnt = sf->glyphmax = info->glyph_cnt; + for ( i=0; i<sf->glyphcnt; ++i ) + if ( (sc = sf->glyphs[i])!=NULL ) { + sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = info->to_order2; + sc->parent = sf; + } + + /* A CFF font could contain type1 charstrings, or a type2 font could use */ + /* the depreciated convention that endchar =~ seac */ + if ( info->cff_length!=0 ) + SFInstanciateRefs(sf); + + for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) { + for ( rf = sf->glyphs[i]->layers[ly_fore].refs, prev=NULL; rf!=NULL; rf = next ) { + next = rf->next; + if ( rf->sc==NULL ) { + if ( prev==NULL ) sf->glyphs[i]->layers[ly_fore].refs = next; + else prev->next = next; + RefCharFree(rf); + } else { + rf->orig_pos = rf->sc->orig_pos; + rf->unicode_enc = rf->sc->unicodeenc; + prev = rf; + } + } + } + sf->map = info->map; + sf->uni_interp = info->uni_interp; + AltUniFigure(sf,sf->map); + NameConsistancyCheck(sf, sf->map); +} + +static char *AxisNameConvert(uint32 tag) { + char buffer[8]; + + if ( tag==CHR('w','g','h','t')) +return( copy("Weight")); + if ( tag==CHR('w','d','t','h')) +return( copy("Width")); + if ( tag==CHR('o','p','s','z')) +return( copy("OpticalSize")); + if ( tag==CHR('s','l','n','t')) +return( copy("Slant")); + + buffer[0] = tag>>24; + buffer[1] = tag>>16; + buffer[2] = tag>>8; + buffer[3] = tag&0xff; + buffer[4] = 0; +return( copy(buffer )); +} + +static struct macname *FindMacName(struct ttfinfo *info, int strid) { + struct macidname *sid; + + for ( sid=info->macstrids; sid!=NULL; sid=sid->next ) { + if ( sid->id == strid ) +return( sid->head ); + } +return( NULL ); +} + +static SplineFont *SFFromTuple(SplineFont *basesf,struct variations *v,int tuple, + MMSet *mm, struct ttfinfo *info) { + SplineFont *sf; + int i; + RefChar *r; + (void)info; /* for -Wall */ + sf = SplineFontEmpty(); + sf->display_size = basesf->display_size; + sf->display_antialias = basesf->display_antialias; + + sf->fontname = MMMakeMasterFontname(mm,tuple,&sf->fullname); + sf->familyname = copy(basesf->familyname); + sf->weight = copy("All"); + sf->italicangle = basesf->italicangle; + sf->strokewidth = basesf->strokewidth; + sf->strokedfont = basesf->strokedfont; + sf->upos = basesf->upos; + sf->uwidth = basesf->uwidth; + sf->ascent = basesf->ascent; + sf->hasvmetrics = basesf->hasvmetrics; + sf->descent = basesf->descent; + sf->kerns = v->tuples[tuple].khead; + sf->vkerns = v->tuples[tuple].vkhead; + sf->map = basesf->map; + sf->mm = mm; + sf->glyphmax = sf->glyphcnt = basesf->glyphcnt; + sf->glyphs = v->tuples[tuple].chars; + sf->layers[ly_fore].order2 = sf->layers[ly_back].order2 = true; + for ( i=0; i<sf->glyphcnt; ++i ) if ( basesf->glyphs[i]!=NULL ) { + SplineChar *sc = sf->glyphs[i]; + sc->orig_pos = i; + sc->parent = sf; + sc->layers[ly_fore].order2 = sc->layers[ly_back].order2 = true; + } + sf->grid.order2 = true; + for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) { + for ( r=sf->glyphs[i]->layers[ly_fore].refs; r!=NULL; r=r->next ) + SCReinstanciateRefChar(sf->glyphs[i],r,ly_fore); + } + + sf->ttf_tables = v->tuples[tuple].cvt; + + v->tuples[tuple].chars = NULL; + v->tuples[tuple].khead = NULL; + v->tuples[tuple].vkhead = NULL; + v->tuples[tuple].cvt = NULL; +return( sf ); +} + +static void MMFillFromVAR(SplineFont *sf, struct ttfinfo *info) { + MMSet *mm = chunkalloc(sizeof(MMSet)); + struct variations *v = info->variations; + int i,j; + + sf->mm = mm; + mm->normal = sf; + mm->apple = true; + mm->axis_count = v->axis_count; + mm->instance_count = v->tuple_count; + mm->instances = galloc(v->tuple_count*sizeof(SplineFont *)); + mm->positions = galloc(v->tuple_count*v->axis_count*sizeof(real)); + for ( i=0; i<v->tuple_count; ++i ) for ( j=0; j<v->axis_count; ++j ) + mm->positions[i*v->axis_count+j] = v->tuples[i].coords[j]; + mm->defweights = gcalloc(v->tuple_count,sizeof(real)); /* Doesn't apply */ + mm->axismaps = gcalloc(v->axis_count,sizeof(struct axismap)); + for ( i=0; i<v->axis_count; ++i ) { + mm->axes[i] = AxisNameConvert(v->axes[i].tag); + mm->axismaps[i].min = v->axes[i].min; + mm->axismaps[i].def = v->axes[i].def; + mm->axismaps[i].max = v->axes[i].max; + if ( v->axes[i].paircount==0 ) { + mm->axismaps[i].points = 3; + mm->axismaps[i].blends = galloc(3*sizeof(real)); + mm->axismaps[i].designs = galloc(3*sizeof(real)); + mm->axismaps[i].blends[0] = -1; mm->axismaps[i].designs[0] = mm->axismaps[i].min; + mm->axismaps[i].blends[1] = 0; mm->axismaps[i].designs[1] = mm->axismaps[i].def; + mm->axismaps[i].blends[2] = 1; mm->axismaps[i].designs[2] = mm->axismaps[i].max; + } else { + mm->axismaps[i].points = v->axes[i].paircount; + mm->axismaps[i].blends = galloc(v->axes[i].paircount*sizeof(real)); + mm->axismaps[i].designs = galloc(v->axes[i].paircount*sizeof(real)); + for ( j=0; j<v->axes[i].paircount; ++j ) { + if ( v->axes[i].mapfrom[j]<=0 ) { + mm->axismaps[i].designs[j] = mm->axismaps[i].def + + v->axes[i].mapfrom[j]*(mm->axismaps[i].def-mm->axismaps[i].min); + } else { + mm->axismaps[i].designs[j] = mm->axismaps[i].def + + v->axes[i].mapfrom[j]*(mm->axismaps[i].max-mm->axismaps[i].def); + } + mm->axismaps[i].blends[j] = v->axes[i].mapto[j]; + } + } + mm->axismaps[i].axisnames = MacNameCopy(FindMacName(info, v->axes[i].nameid)); + } + mm->named_instance_count = v->instance_count; + mm->named_instances = galloc(v->instance_count*sizeof(struct named_instance)); + for ( i=0; i<v->instance_count; ++i ) { + mm->named_instances[i].coords = v->instances[i].coords; + v->instances[i].coords = NULL; + mm->named_instances[i].names = MacNameCopy(FindMacName(info, v->instances[i].nameid)); + } + for ( i=0; i<mm->instance_count; ++i ) + mm->instances[i] = SFFromTuple(sf,v,i,mm,info); + VariationFree(info); +} + +static void SFRelativeWinAsDs(SplineFont *sf) { + if ( !sf->pfminfo.winascent_add || !sf->pfminfo.windescent_add || + !sf->pfminfo.hheadascent_add || !sf->pfminfo.hheaddescent_add ) { + DBounds b; + CIDLayerFindBounds(sf,ly_fore,&b); + if ( !sf->pfminfo.winascent_add ) { + sf->pfminfo.winascent_add = true; + if ( sf->pfminfo.os2_winascent < sf->ascent/8 ) { + /* There was a bug for a while which gave us really bad values */ + sf->pfminfo.os2_winascent = 0; + sf->pfminfo.windescent_add = true; + sf->pfminfo.os2_windescent = 0; + } else + sf->pfminfo.os2_winascent -= b.maxy; + } + if ( !sf->pfminfo.windescent_add ) { + sf->pfminfo.windescent_add = true; + sf->pfminfo.os2_windescent += b.miny; + } + if ( !sf->pfminfo.hheadascent_add ) { + sf->pfminfo.hheadascent_add = true; + sf->pfminfo.hhead_ascent -= b.maxy; + } + if ( !sf->pfminfo.hheaddescent_add ) { + sf->pfminfo.hheaddescent_add = true; + sf->pfminfo.hhead_descent -= b.miny; + } + } + if ( !sf->pfminfo.typoascent_add ) { + sf->pfminfo.typoascent_add = true; + sf->pfminfo.os2_typoascent -= sf->ascent; + } + if ( !sf->pfminfo.typodescent_add ) { + sf->pfminfo.typodescent_add = true; + sf->pfminfo.os2_typodescent -= -sf->descent; + } +} + +static void PsuedoEncodeUnencoded(EncMap *map,struct ttfinfo *info) { + int extras, base; + int i; + + for ( i=0; i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL ) + info->chars[i]->ticked = false; + for ( i=0; i<map->enccount; ++i ) + if ( map->map[i]!=-1 ) + info->chars[map->map[i]]->ticked = true; + extras = 0; + for ( i=0; i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL && !info->chars[i]->ticked ) + ++extras; + if ( extras!=0 ) { + if ( map->enccount<=256 ) + base = 256; + else if ( map->enccount<=65536 ) + base = 65536; + else if ( map->enccount<=17*65536 ) + base = 17*65536; + else + base = map->enccount; + if ( base+extras>map->encmax ) { + map->map = grealloc(map->map,(base+extras)*sizeof(int)); + memset(map->map+map->enccount,-1,(base+extras-map->enccount)*sizeof(int)); + map->encmax = base+extras; + } + map->enccount = base+extras; + extras = 0; + for ( i=0; i<info->glyph_cnt; ++i ) + if ( info->chars[i]!=NULL && !info->chars[i]->ticked ) + map->map[base+extras++] = i; + } +} + +static void MapDoBack(EncMap *map,struct ttfinfo *info) { + int i; + + if ( map==NULL ) /* CID fonts */ +return; + free(map->backmap); /* CFF files have this */ + map->backmax = info->glyph_cnt; + map->backmap = galloc(info->glyph_cnt*sizeof(int)); + memset(map->backmap,-1,info->glyph_cnt*sizeof(int)); + for ( i = map->enccount-1; i>=0; --i ) + if ( map->map[i]>=0 && map->map[i]<info->glyph_cnt ) + if ( map->backmap[map->map[i]]==-1 ) + map->backmap[map->map[i]] = i; +} + +void TTF_PSDupsDefault(SplineFont *sf) { + struct ttflangname *english; + char versionbuf[40]; + + /* Ok, if we've just loaded a ttf file then we've got a bunch of langnames*/ + /* we copied some of them (copyright, family, fullname, etc) into equiv */ + /* postscript entries in the sf. If we then use FontInfo and change the */ + /* obvious postscript entries we are left with the old ttf entries. If */ + /* we generate a ttf file and then load it the old values pop up. */ + /* Solution: Anything we can generate by default should be set to NULL */ + for ( english=sf->names; english!=NULL && english->lang!=0x409; english=english->next ); + if ( english==NULL ) +return; + if ( english->names[ttf_family]!=NULL && + strcmp(english->names[ttf_family],sf->familyname)==0 ) { + free(english->names[ttf_family]); + english->names[ttf_family]=NULL; + } + if ( english->names[ttf_copyright]!=NULL && + strcmp(english->names[ttf_copyright],sf->copyright)==0 ) { + free(english->names[ttf_copyright]); + english->names[ttf_copyright]=NULL; + } + if ( english->names[ttf_fullname]!=NULL && + strcmp(english->names[ttf_fullname],sf->fullname)==0 ) { + free(english->names[ttf_fullname]); + english->names[ttf_fullname]=NULL; + } + if ( sf->subfontcnt!=0 || sf->version!=NULL ) { + if ( sf->subfontcnt!=0 ) + sprintf( versionbuf, "Version %f", sf->cidversion ); + else + sprintf(versionbuf,"Version %.20s ", sf->version); + if ( english->names[ttf_version]!=NULL && + strcmp(english->names[ttf_version],versionbuf)==0 ) { + free(english->names[ttf_version]); + english->names[ttf_version]=NULL; + } + } + if ( english->names[ttf_subfamily]!=NULL && + strcmp(english->names[ttf_subfamily],SFGetModifiers(sf))==0 ) { + free(english->names[ttf_subfamily]); + english->names[ttf_subfamily]=NULL; + } + + /* User should not be allowed any access to this one, not ever */ + free(english->names[ttf_postscriptname]); + english->names[ttf_postscriptname]=NULL; +} + +static SplineFont *SFFillFromTTF(struct ttfinfo *info) { + SplineFont *sf, *_sf; + int i,k; + SplineChar *sc; + struct ttf_table *last[2], *tab, *next; + + + sf = SplineFontEmpty(); + sf->display_size = -default_fv_font_size; +#ifdef LUA_FF_LIB + sf->units_per_em = info->emsize; +#endif + sf->display_antialias = default_fv_antialias; + sf->fontname = info->fontname; + sf->fullname = info->fullname; + sf->familyname = info->familyname; + sf->chosenname = info->chosenname; + sf->onlybitmaps = info->onlystrikes; + sf->layers[ly_fore].order2 = info->to_order2; + sf->layers[ly_back].order2 = info->to_order2; + sf->comments = info->fontcomments; + sf->fontlog = info->fontlog; + sf->cvt_names = info->cvt_names; + + sf->creationtime = info->creationtime; + sf->modificationtime = info->modificationtime; + + sf->design_size = info->design_size; + sf->design_range_bottom = info->design_range_bottom; + sf->design_range_top = info->design_range_top; + sf->fontstyle_id = info->fontstyle_id; + sf->fontstyle_name = info->fontstyle_name; + + sf->gasp_cnt = info->gasp_cnt; + sf->gasp = info->gasp; + sf->MATH = info->math; + + sf->texdata = info->texdata; + + sf->mark_class_cnt = info->mark_class_cnt; + sf->mark_classes = info->mark_classes; + sf->mark_class_names = info->mark_class_names; + + if ( info->fd!=NULL ) { /* Special hack for type42 fonts */ + sf->fontname = copy(info->fd->fontname); + sf->uniqueid = info->fd->uniqueid; + sf->xuid = XUIDFromFD(info->fd->xuid); + if ( info->fd->fontinfo!=NULL ) { + sf->familyname = utf8_verify_copy(info->fd->fontinfo->familyname); + sf->fullname = utf8_verify_copy(info->fd->fontinfo->fullname); + sf->copyright = utf8_verify_copy(info->fd->fontinfo->notice); + sf->weight = utf8_verify_copy(info->fd->fontinfo->weight); + sf->version = utf8_verify_copy(info->fd->fontinfo->version); + sf->italicangle = info->fd->fontinfo->italicangle; + sf->upos = info->fd->fontinfo->underlineposition*(sf->ascent+sf->descent); + sf->uwidth = info->fd->fontinfo->underlinethickness*(sf->ascent+sf->descent); + } + } + + if ( sf->fontname==NULL ) { + sf->fontname = EnforcePostScriptName(sf->fullname); + if ( sf->fontname==NULL ) + sf->fontname = EnforcePostScriptName(sf->familyname); + if ( sf->fontname==NULL ) sf->fontname = EnforcePostScriptName("UntitledTTF"); + } + if ( sf->fullname==NULL ) sf->fullname = copy( sf->fontname ); + if ( sf->familyname==NULL ) sf->familyname = copy( sf->fontname ); + if ( sf->weight==NULL ) { + if ( info->weight != NULL ) + sf->weight = info->weight; + else if ( info->pfminfo.pfmset ) + sf->weight = copy( info->pfminfo.weight <= 100 ? "Thin" : + info->pfminfo.weight <= 200 ? "Extra-Light" : + info->pfminfo.weight <= 300 ? "Light" : + info->pfminfo.weight <= 400 ? "Book" : + info->pfminfo.weight <= 500 ? "Medium" : + info->pfminfo.weight <= 600 ? "Demi" : + info->pfminfo.weight <= 700 ? "Bold" : + info->pfminfo.weight <= 800 ? "Heavy" : + "Black" ); + else + sf->weight = copy(""); + } else + free( info->weight ); + if ( sf->copyright==NULL ) + sf->copyright = info->copyright; + else + free( info->copyright ); + sf->version = info->version; + sf->italicangle = info->italicAngle; + sf->strokewidth = info->strokewidth; + sf->strokedfont = info->strokedfont; + sf->upos = info->upos; + sf->uwidth = info->uwidth; + sf->ascent = info->ascent; + if ( info->vhea_start!=0 && info->vmetrics_start!=0 ) + sf->hasvmetrics = true; + sf->descent = info->descent; + sf->private = info->private; + sf->xuid = info->xuid; + sf->uniqueid = info->uniqueid; + sf->pfminfo = info->pfminfo; + sf->os2_version = info->os2_version; + sf->use_typo_metrics = info->use_typo_metrics; + sf->weight_width_slope_only = info->weight_width_slope_only; + sf->head_optimized_for_cleartype = info->optimized_for_cleartype; + sf->gasp_version = info->gasp_version; + sf->names = info->names; + sf->anchor = info->ahead; + sf->kerns = info->khead; + sf->vkerns = info->vkhead; + sf->possub = info->possub; + sf->sm = info->sm; + sf->features = info->features; + sf->gpos_lookups = info->gpos_lookups; + sf->gsub_lookups = info->gsub_lookups; + + last[0] = sf->ttf_tables; + last[1] = NULL; + for ( tab=info->tabs; tab!=NULL; tab = next ) { + next = tab->next; + if ( tab->tag==CHR('f','p','g','m') || tab->tag==CHR('p','r','e','p') || + tab->tag==CHR('c','v','t',' ') || tab->tag==CHR('m','a','x','p')) { + if ( last[0]==NULL ) + sf->ttf_tables = tab; + else + last[0]->next = tab; + last[0] = tab; + } else { + if ( last[1]==NULL ) + sf->ttf_tab_saved = tab; + else + last[1]->next = tab; + last[1] = tab; + } + tab->next = NULL; + } + + if ( info->twobytesymbol ) + /* rework ms symbol encodings */ + SymbolFixup(info); + if ( info->map==NULL && info->subfonts==NULL ) /* Can happen when reading a ttf from a pdf */ + info->map = EncMapFromEncoding(sf,FindOrMakeEncoding("original")); + if ( info->subfontcnt==0 ) + PsuedoEncodeUnencoded(info->map,info); + MapDoBack(info->map,info); + sf->map = info->map; + sf->cidregistry = info->cidregistry; + sf->ordering = info->ordering; + sf->supplement = info->supplement; + sf->cidversion = info->cidfontversion; + sf->bitmaps = info->bitmaps; + sf->grid = info->guidelines; + sf->horiz_base = info->horiz_base; + sf->vert_base = info->vert_base; + if ( info->layers!=NULL ) { + info->layers[ly_fore] = sf->layers[ly_fore]; + sf->layers[ly_fore].name = NULL; + if ( info->layers[ly_back].name==NULL ) + info->layers[ly_back].name = sf->layers[ly_back].name; + else + free( sf->layers[ly_back].name ); + free( sf->layers ); + sf->layers = info->layers; + sf->layer_cnt = info->layer_cnt; + } + + + for ( i=0; i<info->glyph_cnt; ++i ) if ( info->chars[i]!=NULL ) { + SCOrderAP(info->chars[i]); + } + + if ( info->subfontcnt == 0 ) { + UseGivenEncoding(sf,info); + } else { + sf->subfontcnt = info->subfontcnt; + sf->subfonts = info->subfonts; + free(info->chars); /* This is the GID->char index, don't need it now */ + for ( i=0; i<sf->subfontcnt; ++i ) { + sf->subfonts[i]->cidmaster = sf; + sf->subfonts[i]->hasvmetrics = sf->hasvmetrics; + } + } + TTF_PSDupsDefault(sf); +#if 0 + if ( info->gsub_start==0 && info->mort_start==0 && info->morx_start==0 ) { + /* Get default ligature values, etc. */ + k=0; + do { + _sf = k<sf->subfontcnt?sf->subfonts[k]:sf; + for ( i=0; i<sf->glyphcnt; ++i ) { + if ( _sf->glyphs[i]!=NULL ) /* Might be null in ttc files */ + SCLigDefault(_sf->glyphs[i]); + } + ++k; + } while ( k<sf->subfontcnt ); + } +#endif + + /* I thought the languages were supposed to be ordered, but it seems */ + /* that is not always the case. Order everything, just in case */ + { int isgpos; OTLookup *otl; + for ( isgpos=0; isgpos<2; ++isgpos ) + for ( otl= isgpos? sf->gpos_lookups:sf->gsub_lookups; otl!=NULL; otl=otl->next ) + otl->features = FLOrder(otl->features); + } + + if ( info->variations!=NULL ) + MMFillFromVAR(sf,info); + + if ( info->cff_length!=0 && !sf->layers[ly_fore].order2 ) { + /* Clean up the hint masks, We create an initial hintmask whether we */ + /* need it or not */ + k=0; + do { + _sf = k<sf->subfontcnt?sf->subfonts[k]:sf; + for ( i=0; i<sf->glyphcnt; ++i ) { + if ( (sc = _sf->glyphs[i])!=NULL && !sc->hconflicts && !sc->vconflicts && + sc->layers[ly_fore].splines!=NULL ) { + chunkfree( sc->layers[ly_fore].splines->first->hintmask,sizeof(HintMask) ); + sc->layers[ly_fore].splines->first->hintmask = NULL; + } + } + ++k; + } while ( k<sf->subfontcnt ); + } + SFRelativeWinAsDs(sf); + free(info->savetab); + sf->loadvalidation_state = + (info->bad_ps_fontname ?lvs_bad_ps_fontname:0) | + (info->bad_glyph_data ?lvs_bad_glyph_table:0) | + (info->bad_cff ?lvs_bad_cff_table:0) | + (info->bad_metrics ?lvs_bad_metrics_table:0) | + (info->bad_cmap ?lvs_bad_cmap_table:0) | + (info->bad_embedded_bitmap ?lvs_bad_bitmaps_table:0) | + (info->bad_gx ?lvs_bad_gx_table:0) | + (info->bad_ot ?lvs_bad_ot_table:0) | + (info->bad_os2_version ?lvs_bad_os2_version:0)| + (info->bad_sfnt_header ?lvs_bad_sfnt_header:0); +return( sf ); +} + +SplineFont *_SFReadTTF(FILE *ttf, int flags,enum openflags openflags, char *filename,struct fontdict *fd) { + struct ttfinfo info; + int ret; + + memset(&info,'\0',sizeof(struct ttfinfo)); + info.onlystrikes = (flags&ttf_onlystrikes)?1:0; + info.onlyonestrike = (flags&ttf_onlyonestrike)?1:0; + info.use_typo_metrics = true; + info.openflags = openflags; + info.fd = fd; + ret = readttf(ttf,&info,filename); + if ( !ret ) +return( NULL ); +return( SFFillFromTTF(&info)); +} + +SplineFont *SFReadTTF(char *filename, int flags, enum openflags openflags) { + FILE *ttf; + SplineFont *sf; + char *temp=filename, *pt, *lparen, *rparen; + + pt = strrchr(filename,'/'); + if ( pt==NULL ) pt = filename; + if ( (lparen = strrchr(pt,'('))!=NULL && + (rparen = strrchr(lparen,')'))!=NULL && + rparen[1]=='\0' ) { + temp = copy(filename); + pt = temp + (lparen-filename); + *pt = '\0'; + } + ttf = fopen(temp,"rb"); + if ( temp!=filename ) free(temp); + if ( ttf==NULL ) +return( NULL ); + + sf = _SFReadTTF(ttf,flags,openflags,filename,NULL); + fclose(ttf); +return( sf ); +} + +#ifdef LUA_FF_LIB + +static int readttfinfo(FILE *ttf, struct ttfinfo *info, char *filename) { + + if ( !readttfheader(ttf,info,filename,&info->chosenname)) { +return( 0 ); + } + readttfpreglyph(ttf,info); + if ( info->os2_start!=0 ) + readttfos2metrics(ttf,info); + if ( info->postscript_start!=0 ) { + fseek(ttf,info->postscript_start,SEEK_SET); + (void)getlong(ttf); + info->italicAngle = getfixed(ttf); + } +return( true ); +} + +/* I am not sure what happens to the ttinfo struct's members. + perhaps some need free()-ing +*/ + +void THPatchSplineChar (SplineChar *sc) +{ + DBounds bb; + if (sc->layers!=NULL && sc->layers[ly_fore].splines != NULL) { + if (sc->xmax==0 && sc->ymax==0 && sc->xmin==0 && sc->ymin==0) { + SplineCharFindBounds(sc,&bb); + sc->xmin = bb.minx; + sc->ymin = bb.miny; + sc->xmax = bb.maxx; + sc->ymax = bb.maxy; + } + /* free the curves */ + SplinePointListsFree(sc->layers[ly_fore].splines); + sc->layers[ly_fore].splines=NULL; + } + if (sc->layers[ly_fore].refs!=NULL) { + RefCharsFree(sc->layers[ly_fore].refs); + sc->layers[ly_fore].refs = NULL; + } +} + + +static SplineFont *SFFillFromTTFInfo(struct ttfinfo *info) { + SplineFont *sf ; + + sf = SplineFontEmpty(); + + sf->fontname = info->fontname; + sf->fullname = info->fullname; + sf->familyname = info->familyname; + + if ( info->fd!=NULL ) { /* Special hack for type42 fonts */ + sf->fontname = copy(info->fd->fontname); + if ( info->fd->fontinfo!=NULL ) { + sf->familyname = utf8_verify_copy(info->fd->fontinfo->familyname); + sf->fullname = utf8_verify_copy(info->fd->fontinfo->fullname); + sf->weight = utf8_verify_copy(info->fd->fontinfo->weight); + } + } + if ( sf->fontname==NULL ) sf->fontname = EnforcePostScriptName(sf->fullname); + if ( sf->fontname==NULL ) sf->fontname = EnforcePostScriptName(sf->familyname); + if ( sf->fontname==NULL ) sf->fontname = EnforcePostScriptName("UntitledTTF"); + + if ( sf->fullname==NULL ) sf->fullname = copy( sf->fontname ); + if ( sf->familyname==NULL ) sf->familyname = copy( sf->fontname ); + if ( sf->weight==NULL ) { + if ( info->weight != NULL ) + sf->weight = info->weight; + else if ( info->pfminfo.pfmset ) + sf->weight = copy( info->pfminfo.weight <= 100 ? "Thin" : + info->pfminfo.weight <= 200 ? "Extra-Light" : + info->pfminfo.weight <= 300 ? "Light" : + info->pfminfo.weight <= 400 ? "Book" : + info->pfminfo.weight <= 500 ? "Medium" : + info->pfminfo.weight <= 600 ? "Demi" : + info->pfminfo.weight <= 700 ? "Bold" : + info->pfminfo.weight <= 800 ? "Heavy" : + "Black" ); + else + sf->weight = copy(""); + } else + free( info->weight ); + sf->version = info->version; + sf->italicangle = info->italicAngle; + +return( sf ); +} + +SplineFont *_SFReadTTFInfo(FILE *ttf, int flags,enum openflags openflags, char *filename,struct fontdict *fd) { + struct ttfinfo info; + int ret; + (void)openflags; /* for -Wall */ + memset(&info,'\0',sizeof(struct ttfinfo)); + info.onlystrikes = (flags&ttf_onlystrikes)?1:0; + info.onlyonestrike = (flags&ttf_onlyonestrike)?1:0; + info.use_typo_metrics = true; + info.fd = fd; + ret = readttfinfo(ttf,&info,filename); + if ( !ret ) +return( NULL ); +return( SFFillFromTTFInfo(&info)); +} + +SplineFont *SFReadTTFInfo(char *filename, int flags, enum openflags openflags) { + FILE *ttf; + SplineFont *sf; + char *temp=filename, *pt, *lparen; + + pt = strrchr(filename,'/'); + if ( pt==NULL ) pt = filename; + if ( (lparen=strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) { + temp = copy(filename); + pt = temp + (lparen-filename); + *pt = '\0'; + } + ttf = fopen(temp,"rb"); + if ( temp!=filename ) free(temp); + if ( ttf==NULL ) +return( NULL ); + + sf = _SFReadTTFInfo(ttf,flags,openflags,filename,NULL); + fclose(ttf); +return( sf ); +} +#endif + +SplineFont *_CFFParse(FILE *temp,int len, char *fontsetname) { + struct ttfinfo info; + (void)fontsetname; /* for -Wall */ + + memset(&info,'\0',sizeof(info)); + info.cff_start = 0; + info.cff_length = len; + info.barecff = true; + if ( !readcffglyphs(temp,&info) ) +return( NULL ); +return( SFFillFromTTF(&info)); +} + +SplineFont *CFFParse(char *filename) { + FILE *cff = fopen(filename,"r"); + SplineFont *sf; + long len; + + if ( cff == NULL ) +return( NULL ); + fseek(cff,0,SEEK_END); + len = ftell(cff); + fseek(cff,0,SEEK_SET); + sf = _CFFParse(cff,len,NULL); + fclose(cff); +return( sf ); +} + +char **NamesReadCFF(char *filename) { + FILE *cff = fopen(filename,"rb"); + int32 hdrsize, offsize; + char **fontnames; + + if ( cff==NULL ) +return( NULL ); + if ( getc(cff)!='\1' ) { /* Major version */ + LogError( _("CFF version mismatch\n") ); + fclose(cff); +return( NULL ); + } + getc(cff); /* Minor version */ + hdrsize = getc(cff); + offsize = getc(cff); + if ( hdrsize!=4 ) + fseek(cff,hdrsize,SEEK_SET); + fontnames = readcfffontnames(cff,NULL,NULL); + fclose(cff); +return( fontnames ); +} + +char **NamesReadTTF(char *filename) { + FILE *ttf = fopen(filename,"rb"); + int32 version, cnt, *offsets; + int i,j; + char **ret = NULL; + char *temp; + + if ( ttf==NULL ) +return( NULL ); + version=getlong(ttf); + if ( version==CHR('t','t','c','f')) { + /* TTCF version = */ getlong(ttf); + cnt = getlong(ttf); + offsets = galloc(cnt*sizeof(int32)); + for ( i=0; i<cnt; ++i ) + offsets[i] = getlong(ttf); + ret = galloc((cnt+1)*sizeof(char *)); + for ( i=j=0; i<cnt; ++i ) { + temp = TTFGetFontName(ttf,offsets[i],0); + if ( temp!=NULL ) + ret[j++] = temp; + } + ret[j] = NULL; + free(offsets); + } else { + temp = TTFGetFontName(ttf,0,0); + if ( temp!=NULL ) { + ret = galloc(2*sizeof(char *)); + ret[0] = temp; + ret[1] = NULL; + } + } + fclose(ttf); +return(ret); +} |