/* 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 #include #include #include #include #include #include "ttf.h" extern char *AdobeStandardEncoding[], *AdobeExpertEncoding[]; extern const char *ttfstandardnames[]; extern int ask_user_for_cmap; extern char *SaveTablesPref; #ifdef LUA_FF_LIB SplineFont *_SFReadTTFInfo(FILE *ttf, int flags,enum openflags openflags, char *filename,struct fontdict *fd); void THPatchSplineChar(SplineChar *sc); #endif int THtest_duplicate_glyph_names = 0; int THread_ttf_glyph_data = 0; 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; iis_unicodebmp ) { str = pt = galloc((sizeof(unichar_t)/2)*len+sizeof(unichar_t)); for ( i=0; iunicode!=NULL ) { str = pt = galloc(sizeof(unichar_t)*len+sizeof(unichar_t)); for ( i=0; iunicode[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; iis_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)); } char *TTFGetPSFontName(FILE *ttf,int32 offset,int32 off2) { int i,num; int32 tag, length, stringoffset; int name, len, off; int32 nameoffset = 0; fseek(ttf,offset,SEEK_SET); /* version = */ getlong(ttf); num = getushort(ttf); /* srange = */ getushort(ttf); /* esel = */ getushort(ttf); /* rshift = */ getushort(ttf); for ( i=0; i=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: is not in */ _("%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; isavecnt = 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 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; inumtables; ++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].tagbad_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; inumtables; ++i ) for ( j=i+1; jnumtables; ++j ) { if ( tabs[i].offset>tabs[j].offset ) { temp = tabs[i]; tabs[i] = tabs[j]; tabs[j] = temp; } } for ( i=0; inumtables-1; ++i ) { for ( j=i+1; jnumtables; ++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; inumtables-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; inumtables-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; inumtables; ++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; jsavecnt; ++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; ifeatures; 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; inext = 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; icopyright==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; pathnext = 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; ibad_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=tot ) { IError("Flag count is wrong (or total is): %d %d", i+cnt, tot ); cnt = tot-i-1; } for ( j=0; jorig_pos ); last_pos = 0; for ( i=0; ilayers[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)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; ittf_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 ( endglyph_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; #endif if (THread_ttf_glyph_data) { 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)>(long)(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; iglyph_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; iglyph_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; iglyph_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; ibad_cff = true; while ( i>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; iglyphs.cnt; ++i ) free(dict->glyphs.values[i]); free(dict->glyphs.values); free(dict->glyphs.lens); for ( i=0; ilocal_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; ioffsets[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)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; ixuid[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; ibasefontblend[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; iblendaxistypes[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)bad_cff = true; } else if ( ret==3 ) switch( oval ) { case 6: for ( i=0; ibluevalues[i] = stack[i]; if ( i!=0 ) td->bluevalues[i] += td->bluevalues[i-1]; } break; case 7: for ( i=0; iotherblues[i] = stack[i]; if ( i!=0 ) td->otherblues[i] += td->otherblues[i-1]; } break; case 8: for ( i=0; ifamilyblues[i] = stack[i]; if ( i!=0 ) td->familyblues[i] += td->familyblues[i-1]; } break; case 9: for ( i=0; ifamilyotherblues[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; istemsnaph[i] = stack[i]; if ( i!=0 ) td->stemsnaph[i] += td->stemsnaph[i-1]; } break; case (12<<8)+13: for ( i=0; istemsnapv[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; ifontmatrix_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 *FFgetsid(int sid,char **strings,int scnt,struct ttfinfo *info) { if ( sid==-1 ) return( NULL ); else if ( sidscnt ) { 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; 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; iglyph_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; iglyph_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 && iglyph_cnt; ++i ) map->map[getc(ttf)] = i; } else if ( (format&0x7f)==1 ) { cnt = getc(ttf); pos = 0; for ( i=0; iglyph_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; iglyph_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; icharset[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; icharset[i] = i+227; dict->charset[12] = 13; dict->charset[13] = 14; dict->charset[14] = 15; dict->charset[15] = 99; for ( i=16; icharset[i] = i+223; dict->charset[25] = 27; dict->charset[26] = 28; for ( i=27; icharset[i] = i+222; dict->charset[44] = 109; dict->charset[45] = 110; for ( i=46; icharset[i] = i+221; dict->charset[96] = 158; dict->charset[97] = 155; dict->charset[98] = 163; for ( i=99; icharset[i] = i+220; dict->charset[107] = 150; dict->charset[108] = 164; dict->charset[109] = 169; for ( i=110; icharset[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; icharset[i] = i+227; dict->charset[12] = 13; dict->charset[13] = 14; dict->charset[14] = 15; dict->charset[15] = 99; for ( i=16; icharset[i] = i+223; dict->charset[25] = 27; dict->charset[26] = 28; for ( i=27; icharset[i] = i+222; dict->charset[44] = 109; dict->charset[45] = 110; for ( i=46; icharset[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; icharset[i] = i+260; dict->charset[67] = 150; dict->charset[68] = 164; dict->charset[69] = 169; for ( i=110; icharset[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; icharset[i] = getushort(ttf); } else if ( format==1 ) { for ( i = 1; icharset[i++] = getushort(ttf); cnt = getc(ttf); for ( j=0; jcharset[i++] = ++first; } } else if ( format==2 ) { for ( i = 1; icharset[i++] = getushort(ttf); cnt = getushort(ttf); for ( j=0; jcharset[i++] = ++first; } } else { LogError( _("Unexpected charset format in cff: %d\n"), format ); if ( info!=NULL ) info->bad_cff = true; } } while ( icharset[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 ) { 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[20]; 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[20]; 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[20]; 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(FFgetsid(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(FFgetsid(subdict->copyright,strings,scnt,info)); else sf->copyright = utf8_verify_copy(FFgetsid(subdict->notice,strings,scnt,info)); sf->familyname = utf8_verify_copy(FFgetsid(subdict->familyname,strings,scnt,info)); sf->fullname = utf8_verify_copy(FFgetsid(subdict->fullname,strings,scnt,info)); sf->weight = utf8_verify_copy(FFgetsid(subdict->weight,strings,scnt,info)); sf->version = utf8_verify_copy(FFgetsid(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(FFgetsid(dict->copyright,strings,scnt,info)); else if ( dict->notice!=-1 ) info->copyright = utf8_verify_copy(FFgetsid(dict->notice,strings,scnt,info)); if ( dict->familyname!=-1 ) { free(info->familyname); info->familyname = utf8_verify_copy(FFgetsid(dict->familyname,strings,scnt,info)); } if ( dict->fullname!=-1 ) { free(info->fullname); info->fullname = utf8_verify_copy(FFgetsid(dict->fullname,strings,scnt,info)); } if ( dict->weight!=-1 ) { free(info->weight); info->weight = utf8_verify_copy(FFgetsid(dict->weight,strings,scnt,info)); } if ( dict->version!=-1 ) { free(info->version); info->version = utf8_verify_copy(FFgetsid(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(FFgetsid(dict->ros_registry,strings,scnt,info)); info->ordering = copy(FFgetsid(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; iglyph_cnt; ++i ) { info->chars[i] = PSCharStringToBB( dict->glyphs.values[i], dict->glyphs.lens[i],&pscontext, subrs,gsubrs,FFgetsid(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; iglyph_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; iglyph_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] = PSCharStringToBB( 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; iglyph_cnt ; ++i ) if ( info->chars[i]!=NULL ) info->chars[i]->orig_pos = i; } if ( info->to_order2 ) { for ( i=0; iglyph_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; ityp1_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; iwidth_cnt && iglyph_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; jglyph_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; iglyphcnt; ++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; iglyph_cnt; ++i ) if ( info->chars[i]!=NULL ) { cnt = 0; scaled_sum = 0; for ( bdf=info->bitmaps; bdf->next!=NULL; bdf=bdf->next ) { if ( iglyphcnt && 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; iglyph_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; jglyph_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 */; #ifdef FROM_CJK_ICONV } 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); #else } else { enc = badencoding(info); #endif } 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; iglyph_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; jglyph_cnt && curgid>=0) info->inuse[curgid] = 1; } else if ( justinuse==git_justinuse ) { if ( curgidglyph_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 ( 0xe668enccount && map->map[0xe668]!=-1 && info->chars[map->map[0xe668]]->unicodeenc=='b' ) ++cnt; if ( 0xe3c8enccount && map->map[0xe626]!=-1 && info->chars[map->map[0xe626]]->unicodeenc==0xe626 ) ++cnt; if ( 0xe3c8enccount && map->map[0xe3c8]!=-1 && info->chars[map->map[0xe3c8]]->unicodeenc==0x29e1 ) ++cnt; if ( 0x2A7Cenccount && map->map[0x2A7C]!=-1 && info->chars[map->map[0x2A7C]]->unicodeenc==0xE32A ) ++cnt; if ( 0x2920enccount && 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; ienc_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; iencoding_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; iencoding_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; iis_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; iunicode; if ( trans==NULL && dcmap[dc].platform==1 ) trans = MacEncToUnicode(dcmap[dc].specific,dcmap[dc].lang-1); for ( i=0; i<256 && iglyph_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]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; iglyph_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 && lencenccount ) 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 ( tempbad_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 && lencenccount ) 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; iinuse[getushort(ttf)]= 1; else { for ( i=0; ichars[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=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+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 && indexglyph_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 && lencenccount ) map->map[lenc] = index; } } } else { int k = table[i]; for ( j=0; j=cnt ) index = 0; else if ( (index = glyphs[subheads[k].rangeoff+j])!= 0 ) index = (uint16) (index+subheads[k].delta); if ( index!=0 && indexglyph_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 && lencenccount ) 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; iinuse[getushort(ttf)]= 1; else for ( i=0; ichars[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 && i>=0) map->map[i] = startglyph+i-start; } } } } } free(cmap_encs); if ( info->chars!=NULL ) for ( i=0; iglyph_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; iglyph_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; 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=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]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; iglyph_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; inext; ++i ) { int gid = (intpt) (chars->values[i]); if ( gid>=0 && gidglyph_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]->unicodeencenccount) map->map[ info->chars[gid]->unicodeenc ] = gid; chars->keys[i] = NULL; chars->values[i] = NULL; } else chars->values[i] = NULL; } } for ( i=0; iglyph_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<0 ) { /* Do this later */; name = NULL; } else { name = StdGlyphName(buffer,info->chars[i]->unicodeenc,info->uni_interp,NULL); if (THtest_duplicate_glyph_names && anynames ) { for ( j=0; jglyph_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 */ /* 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; iglyph_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; igasp[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; iglyph_cnt; ++i ) if ( info->chars[i]!=NULL ) info->chars[i]->ticked = false; for ( i=0; iglyph_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 ? "" : 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; isavecnt; ++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; ienccount; ++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 ; gidglyphcnt; ++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; iglyphcnt; ++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; iglyphcnt; ++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; iglyphcnt; ++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; iglyphcnt; ++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; ituple_count; ++i ) for ( j=0; jaxis_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; iaxis_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; jaxes[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; iinstance_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; iinstance_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; iglyph_cnt; ++i ) if ( info->chars[i]!=NULL ) info->chars[i]->ticked = false; for ( i=0; ienccount; ++i ) if ( map->map[i]!=-1 ) info->chars[map->map[i]]->ticked = true; extras = 0; for ( i=0; iglyph_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; iglyph_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]glyph_cnt ) if ( map->backmap[map->map[i]]==-1 ) map->backmap[map->map[i]] = i; } 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; iglyph_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; isubfontcnt; ++i ) { sf->subfonts[i]->cidmaster = sf; sf->subfonts[i]->hasvmetrics = sf->hasvmetrics; } } /* 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 = ksubfontcnt?sf->subfonts[k]:sf; for ( i=0; iglyphcnt; ++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 ( ksubfontcnt ); } 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, *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 = _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