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