Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
6401 lines (6040 sloc) 201 KB
/* Copyright (C) 2000-2012 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 "fontforge.h"
#include "splinefont.h"
#include <chardata.h>
#include <utype.h>
#include <ustring.h>
#include <math.h>
#include <locale.h>
#include <gwidget.h>
#include "ttf.h"
#include "scripting.h"
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 { const 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 const 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) {
const 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 = malloc(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 ) {
fseek(ttf, pos, SEEK_SET);
return( NULL );
}
if ( enc->is_unicodebmp ) {
str = pt = malloc((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 = malloc(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 = malloc(inlen), *cpt;
ICONV_CONST char *in = cstr;
char *out;
for ( cpt=cstr, i=0; i<len; ++i )
*cpt++ = getc(ttf);
str = malloc(outlen+sizeof(unichar_t));
out = (char *) str;
iconv(enc->tounicode,&in,&inlen,&out,&outlen);
out[0] = '\0'; out[1] = '\0';
out[2] = '\0'; out[3] = '\0';
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, nameoffset, namelength, stringoffset;
int plat, spec, lang, name, len, off, val;
int fullval, fullstr, fulllen, famval, famstr, famlen;
Encoding *enc;
int fullplat, fullspec, fulllang, famplat, famspec, famlang;
int locale = MSLanguageFromLocale();
int maclang = WinLangToMac(locale);
long ttfFileSize;
/* Determine file size to check table offset bounds */
fseek(ttf,0,SEEK_END);
ttfFileSize = ftell(ttf);
fseek(ttf,offset,SEEK_SET);
/* version = */ getlong(ttf);
num = getushort(ttf);
/* srange = */ getushort(ttf);
/* esel = */ getushort(ttf);
/* rshift = */ getushort(ttf);
if ( num == EOF || feof(ttf) || num < 0 || num >= 0xFFFF)
return( NULL );
for ( i=0; i<num; ++i ) {
tag = getlong(ttf);
/* checksum = */ getlong(ttf);
nameoffset = off2+getlong(ttf);
namelength = getlong(ttf);
if ( feof(ttf) )
return( NULL );
if ( tag==CHR('n','a','m','e'))
break;
}
if ( i==num )
return( NULL );
if ( nameoffset+namelength > ttfFileSize )
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));
}
/* Chooses which font to open from a TTC TrueType Collection font file. */
/* */
/* There are five ways that one enclosed font is selected: */
/* 1) there is only one font enclosed, so we force defaulting to that one.*/
/* 2a) the filename has a font index appended, we choose that N'th font. */
/* 2b) the filename has a font name appended, we try to match that name */
/* in list of discovered font names and select that named font. */
/* 3) the user is prompted with a list of all discovered font names, and */
/* asked to select one, and then that N'th font is chosen. */
/* 4) when there is no UI, then font index zero is used. */
/* */
/* On failure and no font is chosen, returns false. */
/* */
/* On success, true is returned. The chosen font name (allocated) pointer */
/* is returned via 'chosenname'. Additionally, the file position is set */
/* pointing to the chosen TTF font offset table, ready for reading the */
/* TTF header. */
/* */
/* Example filename strings with appended font selector: */
/* ./tests/fonts/mingliu.windows.ttc(PMingLiU) */
/* ./tests/fonts/mingliu.windows.ttc(1) */
/* */
/* 'offsets' is a list of file offsets to each enclosed TTF offset table. */
/* 'names' is a list of font names as found in each enclosed name table. */
/* 'names' is used to search for a matching font name, or to present as a */
/* list to the user via ff_choose() to select from. */
/* Once the chosen font index is determined, offsets[choice] is used to */
/* call fseek() to position to the chosen TTF header offset table. Then */
/* the chosen font name is copied into 'chosenname'. */
static int PickTTFFont(FILE *ttf,char *filename,char **chosenname) {
int32 *offsets, cnt, i, choice;
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 = malloc(cnt*sizeof(int32));
for ( i=0; i<cnt; ++i )
offsets[i] = getlong(ttf);
names = malloc(cnt*sizeof(char *));
for ( i=0; i<cnt; ++i ) {
names[i] = TTFGetFontName(ttf,offsets[i],0);
if ( names[i]==NULL )
asprintf(&names[i], "<Unknown font name %d>", i+1);
}
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 = copyn(lparen+1, rparen-lparen-1);
for ( choice=cnt-1; choice>=0; --choice )
if ( names[choice]!=NULL )
if ( strcmp(names[choice],find)==0 )
break;
if ( choice==-1 ) {
char *end;
choice = strtol(find,&end,10);
if ( *end!='\0' )
choice = -1;
else if ( choice < 0 || choice >= cnt )
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 if ( no_windowing_ui )
choice = 0;
else
choice = ff_choose(_("Pick a font, any font..."),(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
if ( choice < -1 || choice >= cnt )
choice = -1;
if ( choice!=-1 ) {
/* position file to start of the chosen TTF font header */
fseek(ttf,offsets[choice],SEEK_SET);
*chosenname = names[choice];
names[choice] = NULL;
}
for ( i=0; i<cnt; ++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 = calloc(cnt+1,sizeof(unichar_t *));
for ( i=0; i<cnt; ++i )
names[i] = uc_copy(fontnames[i]);
if ( no_windowing_ui )
choice = 0;
else
choice = ff_choose(_("Pick a font, any font..."),
(const char **) names,cnt,0,_("There are multiple fonts in this file, pick one"));
for ( i=0; i<cnt; ++i )
free(names[i]);
free(names);
return( choice );
}
static void ParseSaveTablesPref(struct ttfinfo *info) {
char *pt, *spt;
int cnt;
if (info->openflags & of_all_tables) {
info->savecnt = info->numtables;
info->savetab = calloc(info->savecnt,sizeof(struct savetab));
} else {
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 = calloc(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 uint32 regionchecksum(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 = malloc(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 ( regionchecksum(ttf,0,-1)!=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 ( regionchecksum(ttf,tabs[i].offset,tabs[i].length)!=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;
default:
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; const 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, offset, length, version;
uint32 tag;
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);
if ( offset+length > info->ttfFileSize ) {
LogError(_("Table '%c%c%c%c' extends beyond end of file and must be ignored."),
tag>>24, tag>>16, tag>>8, tag );
info->bad_sfnt_header = true;
continue;
}
#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('J','S','T','F'):
info->jstf_start = offset;
info->jstf_length = length;
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 */
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:
if (info->openflags & of_all_tables) {
info->savetab[i].offset = offset;
info->savetab[i].tag = tag;
info->savetab[i].len = length;
}
else {
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 i, date[4];
/* TTFs have creation and modification timestamps in the 'head' table. */
/* These are 64 bit values in network order / big-endian and denoted */
/* as 'LONGDATETIME'. */
/* These timestamps are in "number of seconds since 00:00 1904-01-01", */
/* noted some places as a Mac OS epoch time value. We use Unix epoch */
/* timestamps which are "number of seconds since 00:00 1970-01-01". */
/* The difference between these two epoch values is a constant number */
/* of seconds, and so we convert from Mac to Unix time by simple */
/* subtraction of that constant difference. */
/* (31781 * 65536) + 45184 = 2082844800 secs is 24107 days */
int date1970[4] = {45184, 31781, 0, 0};
/* As there was not (nor still is?) a portable way to do 64-bit math aka*/
/* "long long" the code below works on 16-bit slices of the full value. */
/* The lowest 16 bits is operated on, then the next 16 bits, and so on. */
date[3] = getushort(ttf);
date[2] = getushort(ttf);
date[1] = getushort(ttf);
date[0] = getushort(ttf);
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) =
(((long long) date[3])<<48) |
(((long long) date[2])<<32) |
( 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,SEEK_SET); /* skip over the version number */
info->sfntRevision = getlong(ttf);
(void) getlong(ttf);
(void) getlong(ttf);
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<8; ++i )
getushort(ttf);
for ( i=0; i<4; ++i )
info->fbb[i] = (short) 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 = (short) getushort(ttf);
info->advanceWidthMax = getushort(ttf);
info->pfminfo.hheadset = true;
/*info->ascent = info->pfminfo.hhead_ascent;*/
for ( i=0; i<11; ++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 = malloc(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=malloc(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 strlength, 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,strlength,stroff,spec,language);
return;
} else if ( id<0 || id>=ttf_namemax )
return;
str = _readencstring(ttf,stroff,strlength,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));
} 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 if ( running_script )
ret = 3;
else {
char *buts[5];
buts[0] = _("Use _First");
buts[1] = _("First to _All");
buts[2] = _("Second _to All");
buts[3] = _("Use _Second");
buts[4] = NULL;
ret = ff_ask(_("Multiple names for language"),(const char **)buts,0,3,
_("The 'name' table contains (at least) two strings for the %s in language %s, the first '%.12s...' the second '%.12s...'.\nWhich do you prefer?"),
TTFNameIds(id),MSLangString(language),
cur->names[id],str);
if ( ret==1 || ret==2 )
info->dupnamestate = ret;
}
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;
} 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->nextcpindex<0xfffe )
from->nonextcp = false;
if ( to->me.x!=to->prevcp.x || to->me.y!=to->prevcp.y || from->nextcpindex<0xfffe )
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 ) {
sp->noprevcp = false;
if ( cur->last!=NULL ) {
cur->last->nonextcp = false;
FigureControls(cur->last,sp,&pts[i-1],is_order2);
cur->last->nonextcp = false; // FigureControls reads and changes, so we do this twice.
}
}
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;
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, int gbb[4]) {
uint16 *endpt = malloc((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 = malloc(sizeof(BasePoint));
} else {
tot = endpt[path_cnt-1]+1;
pts = malloc(tot*sizeof(BasePoint));
}
len = getushort(ttf);
instructions = malloc(len);
for ( i=0; i<len; ++i )
instructions[i] = getc(ttf);
flags = malloc(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;
if ( (last_pos<gbb[0] || last_pos>gbb[2]) && ( flags[i]&_On_Curve )) {
if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
info->bad_glyph_data = true;
if ( !(info->openflags&of_fontlint) )
LogError(_(" Subsequent errors will not be reported.\n") );
info->gbbcomplain = true;
}
}
}
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;
if (( last_pos<gbb[1] || last_pos>gbb[3]) && ( flags[i]&_On_Curve ) ) {
if ( !info->gbbcomplain || (info->openflags&of_fontlint)) {
LogError(_("A point in GID %d is outside the glyph bounding box\n"), sc->orig_pos );
info->bad_glyph_data = true;
if ( !(info->openflags&of_fontlint) )
LogError(_(" Subsequent errors will not be reported.\n") );
info->gbbcomplain = true;
}
}
}
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);
SCCategorizePoints(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;
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 ( 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 composite 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 = malloc(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,uint32 start, uint32 end,int gid) {
int path_cnt;
SplineChar *sc = SplineCharCreate(2);
int gbb[4];
sc->layers[ly_fore].background = 0;
sc->layers[ly_back].background = 1;
sc->unicodeenc = -1;
sc->vwidth = info->emsize;
sc->orig_pos = gid;
if ( end>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);
gbb[0] = sc->lsidebearing = (short) getushort(ttf);
gbb[1] = (short) getushort(ttf);
gbb[2] = (short) getushort(ttf);
gbb[3] = (short) getushort(ttf);
if ( info->head_start!=0 && ( gbb[0]<info->fbb[0] || gbb[1]<info->fbb[1] ||
gbb[2]>info->fbb[2] || gbb[3]>info->fbb[3])) {
if ( !info->bbcomplain || (info->openflags&of_fontlint)) {
LogError(_("Glyph bounding box data exceeds font bounding box data for GID %d\n"), gid );
info->bad_glyph_data = true;
if ( !(info->openflags&of_fontlint) )
LogError(_(" Subsequent errors will not be reported.\n") );
info->bbcomplain = true;
}
}
if ( path_cnt>=0 )
readttfsimpleglyph(ttf,info,sc,path_cnt,gbb);
else
readttfcompositglyph(ttf,info,sc,info->glyph_start+end);
/* I don't check that composite glyphs fit in the bounding box */
/* because the components may not have been read in yet */
/* I'll check against the font bb later, if validation mode */
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;
}
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 = malloc((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 = calloc(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 = calloc(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);
if ( info->morx_start!=0 || info->mort_start!=0 )
readttfmort_glyphsused(ttf,info);
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;
uint32 i,j;
if ( cnt!=NULL ) *cnt = count;
if ( count==0 )
return( NULL );
offsets = malloc((count+1)*sizeof(uint32));
offsize = getc(ttf);
for ( i=0; i<=count; ++i )
offsets[i] = getoffset(ttf,offsize);
names = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
for ( j=0; j<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") );
for (;;) {
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 = malloc(count*sizeof(int));
subs->values = malloc(count*sizeof(uint8 *));
offsets = malloc((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] = malloc(offsets[i+1]-offsets[i]+1);
for ( j=0; j+offsets[i]<offsets[i+1]; ++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] = malloc(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 = calloc(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];
}
if ( i==0 ) td->bluevalues[0] = 1234567; /* Marker for an empty arry, which is legal, and different from no array */
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];
}
if ( i==0 ) td->otherblues[0] = 1234567;
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];
}
if ( i==0 ) td->familyblues[0] = 1234567;
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];
}
if ( i==0 ) td->familyotherblues[0] = 1234567;
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];
}
if ( i==0 ) td->stemsnaph[0] = 1234567;
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];
}
if ( i==0 ) td->stemsnapv[0] = 1234567;
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 = malloc((count+1)*sizeof(uint32));
offsize = getc(ttf);
for ( i=0; i<=count; ++i )
offsets[i] = getoffset(ttf,offsize);
dicts = malloc((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;
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 ? (char **)AdobeStandardEncoding : (char **)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);
/* Mask off high (additional encoding bit) and check format type */
if ( (format&0x7f)==0 ) {
/* format 0 is a 1-1 map of glyph_id to code, starting with id 1 */
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);
/* CFF encodings start with glyph_id 1 since 0 is always .notdef */
pos = 1;
/* Parse format 1 code ranges */
for ( i=0; i<cnt ; ++i ) {
/* next byte is code of first character in range */
first = getc(ttf);
/* next byte is the number of additional characters in range */
last = first + getc(ttf);
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 additional encoding bit set, add all additional encodings */
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 = malloc(len*sizeof(uint16));
for ( i=0; i<len && i<=228; ++i )
dict->charset[i] = i;
} else if ( dict->charsetoff==1 ) {
/* Expert charset */
dict->charset = malloc((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 = malloc((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[