Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
9562 lines (8999 sloc)
287 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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-config.h> | |
#include "sfd.h" | |
#include "autohint.h" | |
#include "baseviews.h" | |
#include "bvedit.h" | |
#include "cvimages.h" | |
#include "cvundoes.h" | |
#include "encoding.h" | |
#include "ffglib.h" | |
#include "fontforge.h" | |
#include "fvfonts.h" | |
#include "gfile.h" | |
#include "gutils.h" | |
#include "gwidget.h" | |
#include "lookups.h" | |
#include "mem.h" | |
#include "namelist.h" | |
#include "parsettf.h" | |
#include "psread.h" | |
#include "sfd1.h" | |
#include "splinefill.h" | |
#include "splinefont.h" | |
#include "splineorder2.h" | |
#include "splinesaveafm.h" | |
#include "splineutil.h" | |
#include "splineutil2.h" | |
#include "tottfgpos.h" | |
#include "ttfinstrs.h" | |
#include "ustring.h" | |
#include "utype.h" | |
#include "views.h" | |
#include <dirent.h> | |
#include <limits.h> /* For NAME_MAX or _POSIX_NAME_MAX */ | |
#include <locale.h> | |
#include <math.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#ifdef _WIN32 | |
#include <windows.h> | |
#endif | |
#ifndef NAME_MAX | |
# ifndef _POSIX_NAME_MAX | |
# define _POSIX_NAME_MAX 512 | |
# endif | |
# define NAME_MAX _POSIX_NAME_MAX | |
#endif | |
int UndoRedoLimitToSave = 0; | |
int UndoRedoLimitToLoad = 0; | |
static const char *joins[] = { "miter", "round", "bevel", "inher", NULL }; | |
static const char *caps[] = { "butt", "round", "square", "inher", NULL }; | |
static const char *spreads[] = { "pad", "reflect", "repeat", NULL }; | |
int prefRevisionsToRetain = 32; | |
#ifndef _NO_LIBPNG | |
int WritePNGInSFD = true; | |
#endif | |
#define SFD_PTFLAG_TYPE_MASK 0x3 | |
#define SFD_PTFLAG_IS_SELECTED 0x4 | |
#define SFD_PTFLAG_NEXTCP_IS_DEFAULT 0x8 | |
#define SFD_PTFLAG_PREVCP_IS_DEFAULT 0x10 | |
#define SFD_PTFLAG_ROUND_IN_X 0x20 | |
#define SFD_PTFLAG_ROUND_IN_Y 0x40 | |
#define SFD_PTFLAG_INTERPOLATE 0x80 | |
#define SFD_PTFLAG_INTERPOLATE_NEVER 0x100 | |
#define SFD_PTFLAG_PREV_EXTREMA_MARKED_ACCEPTABLE 0x200 | |
#define SFD_PTFLAG_FORCE_OPEN_PATH 0x400 | |
/* I will retain this list in case there are still some really old sfd files */ | |
/* including numeric encodings. This table maps them to string encodings */ | |
static const char *charset_names[] = { | |
"custom", | |
"iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", | |
"iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso8859-10", | |
"iso8859-11", "iso8859-13", "iso8859-14", "iso8859-15", "iso8859-16", | |
"koi8-r", | |
"jis201", | |
"win", "mac", "symbol", "zapfding", "adobestandard", | |
"jis208", "jis212", "ksc5601", "gb2312", "big5", "big5hkscs", "johab", | |
"unicode", "unicode4", "sjis", "wansung", "gb2312pk", NULL}; | |
static const char *unicode_interp_names[] = { "none", "adobe", "greek", | |
"japanese", "tradchinese", "simpchinese", "korean", "ams", NULL }; | |
/* sfdir files and extensions */ | |
#define FONT_PROPS "font.props" | |
#define STRIKE_PROPS "strike.props" | |
#define EXT_CHAR '.' | |
#define GLYPH_EXT ".glyph" | |
#define BITMAP_EXT ".bitmap" | |
#define STRIKE_EXT ".strike" | |
#define SUBFONT_EXT ".subfont" | |
#define INSTANCE_EXT ".instance" | |
signed char inbase64[256] = { | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, | |
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, | |
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, | |
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 | |
}; | |
static char base64[64] = { | |
'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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; | |
static const char *end_tt_instrs = "EndTTInstrs"; | |
static void SFDConsumeUntil( FILE *sfd, const char** terminators ); | |
static void SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids); | |
static void SFDDumpGuidelines(FILE *sfd,GuidelineSet *gl); | |
static RefChar *SFDGetRef(FILE *sfd, int was_enc); | |
static void SFDDumpImage(FILE *sfd,ImageList *img); | |
#ifndef _NO_LIBPNG | |
static void SFDDumpImagePNG(FILE *sfd,ImageList *img); | |
#endif | |
static AnchorPoint *SFDReadAnchorPoints(FILE *sfd,SplineChar *sc,AnchorPoint** alist, AnchorPoint *lastap); | |
static GuidelineSet *SFDReadGuideline(FILE *sfd, GuidelineSet **gll, GuidelineSet *lastgl); | |
static void SFDDumpTtfInstrs(FILE *sfd,SplineChar *sc); | |
static void SFDDumpTtfInstrsExplicit(FILE *sfd,uint8_t *ttf_instrs, int16_t ttf_instrs_len ); | |
static void SFDDumpHintList(FILE *sfd,const char *key, StemInfo *h); | |
static void SFDDumpDHintList( FILE *sfd,const char *key, DStemInfo *d ); | |
static StemInfo *SFDReadHints(FILE *sfd); | |
static DStemInfo *SFDReadDHints( SplineFont *sf,FILE *sfd,int old ); | |
static int PeekMatch(FILE *stream, const char * target) { | |
// This returns 1 if target matches the next characters in the stream. | |
int pos1 = 0; | |
int lastread = getc(stream); | |
while (target[pos1] != '\0' && lastread != EOF && lastread == target[pos1]) { | |
pos1 ++; lastread = getc(stream); | |
} | |
int rewind_amount = pos1 + ((lastread == EOF) ? 0 : 1); | |
fseek(stream, -rewind_amount, SEEK_CUR); | |
return (target[pos1] == '\0'); | |
} | |
static void utf7_encode(FILE *sfd,long ch) { | |
putc(base64[(ch>>18)&0x3f],sfd); | |
putc(base64[(ch>>12)&0x3f],sfd); | |
putc(base64[(ch>>6)&0x3f],sfd); | |
putc(base64[ch&0x3f],sfd); | |
} | |
static char *base64_encode(char *ostr, long ch) { | |
*ostr++ = base64[(ch>>18)&0x3f]; | |
*ostr++ = base64[(ch>>12)&0x3f]; | |
*ostr++ = base64[(ch>> 6)&0x3f]; | |
*ostr++ = base64[(ch )&0x3f]; | |
return( ostr ); | |
} | |
static void SFDDumpUTF7Str(FILE *sfd, const char *_str) { | |
int ch, prev_cnt=0, prev=0, in=0; | |
const unsigned char *str = (const unsigned char *) _str; | |
putc('"',sfd); | |
if ( str!=NULL ) while ( (ch = *str++)!='\0' ) { | |
/* Convert from utf8 to ucs4 */ | |
if ( ch<=127 ) | |
/* Done */; | |
else if ( ch<=0xdf && *str!='\0' ) { | |
ch = ((ch&0x1f)<<6) | (*str++&0x3f); | |
} else if ( ch<=0xef && *str!='\0' && str[1]!='\0' ) { | |
ch = ((ch&0xf)<<12) | ((str[0]&0x3f)<<6) | (str[1]&0x3f); | |
str += 2; | |
} else if ( *str!='\0' && str[1]!='\0' && str[2]!='\0' ) { | |
int w = ( ((ch&0x7)<<2) | ((str[0]&0x30)>>4) )-1; | |
int s1, s2; | |
s1 = (w<<6) | ((str[0]&0xf)<<2) | ((str[1]&0x30)>>4); | |
s2 = ((str[1]&0xf)<<6) | (str[2]&0x3f); | |
ch = (s1*0x400)+s2 + 0x10000; | |
str += 3; | |
} else { | |
/* illegal */ | |
} | |
if ( ch<127 && ch!='\n' && ch!='\r' && ch!='\\' && ch!='~' && | |
ch!='+' && ch!='=' && ch!='"' ) { | |
if ( prev_cnt!=0 ) { | |
prev<<= (prev_cnt==1?16:8); | |
utf7_encode(sfd,prev); | |
prev_cnt=prev=0; | |
} | |
if ( in ) { | |
if ( inbase64[ch]!=-1 || ch=='-' ) | |
putc('-',sfd); | |
in = 0; | |
} | |
putc(ch,sfd); | |
} else if ( ch=='+' && !in ) { | |
putc('+',sfd); | |
putc('-',sfd); | |
} else if ( prev_cnt== 0 ) { | |
if ( !in ) { | |
putc('+',sfd); | |
in = 1; | |
} | |
prev = ch; | |
prev_cnt = 2; /* 2 bytes */ | |
} else if ( prev_cnt==2 ) { | |
prev<<=8; | |
prev += (ch>>8)&0xff; | |
utf7_encode(sfd,prev); | |
prev = (ch&0xff); | |
prev_cnt=1; | |
} else { | |
prev<<=16; | |
prev |= ch; | |
utf7_encode(sfd,prev); | |
prev_cnt = prev = 0; | |
} | |
} | |
if ( prev_cnt==2 ) { | |
prev<<=8; | |
utf7_encode(sfd,prev); | |
} else if ( prev_cnt==1 ) { | |
prev<<=16; | |
utf7_encode(sfd,prev); | |
} | |
putc('"',sfd); | |
} | |
char *utf8toutf7_copy(const char *_str) { | |
int ch, prev_cnt=0, prev=0, in=0; | |
const unsigned char *str = (const unsigned char *) _str; | |
int i, len; | |
char *ret=NULL, *ostr=NULL; | |
if ( str==NULL ) | |
return( NULL ); | |
for ( i=0; i<2; ++i ) { | |
str = (const unsigned char *) _str; | |
len= prev_cnt= prev= in=0; | |
while ( (ch = *str++)!='\0' ) { | |
/* Convert from utf8 to ucs2 */ | |
if ( ch<=127 ) | |
/* Done */; | |
else if ( ch<=0xdf && *str!='\0' ) { | |
ch = ((ch&0x1f)<<6) | (*str++&0x3f); | |
} else if ( ch<=0xef && *str!='\0' && str[1]!='\0' ) { | |
ch = ((ch&0xf)<<12) | ((str[0]&0x3f)<<6) | (str[1]&0x3f); | |
str += 2; | |
} else if ( *str!='\0' && str[1]!='\0' && str[2]!='\0' ) { | |
int w = ( ((ch&0x7)<<2) | ((str[0]&0x30)>>4) )-1; | |
int s1, s2; | |
s1 = (w<<6) | ((str[0]&0xf)<<2) | ((str[1]&0x30)>>4); | |
s2 = ((str[1]&0xf)<<6) | (str[2]&0x3f); | |
ch = (s1*0x400)+s2 + 0x10000; | |
str += 3; | |
} else { | |
/* illegal */ | |
} | |
if ( ch<127 && ch!='\n' && ch!='\r' && ch!='\\' && ch!='~' && | |
ch!='+' && ch!='=' && ch!='"' ) { | |
if ( prev_cnt!=0 ) { | |
if ( i ) { | |
prev<<= (prev_cnt==1?16:8); | |
ostr = base64_encode(ostr,prev); | |
prev_cnt=prev=0; | |
} else | |
len += 4; | |
} | |
if ( in ) { | |
if ( inbase64[ch]!=-1 || ch=='-' ) { | |
if ( i ) | |
*ostr++ = '-'; | |
else | |
++len; | |
} | |
in = 0; | |
} | |
if ( i ) | |
*ostr++ = ch; | |
else | |
++len; | |
} else if ( ch=='+' && !in ) { | |
if ( i ) { | |
*ostr++ = '+'; | |
*ostr++ = '-'; | |
} else | |
len += 2; | |
} else if ( prev_cnt== 0 ) { | |
if ( !in ) { | |
if ( i ) | |
*ostr++ = '+'; | |
else | |
++len; | |
in = 1; | |
} | |
prev = ch; | |
prev_cnt = 2; /* 2 bytes */ | |
} else if ( prev_cnt==2 ) { | |
prev<<=8; | |
prev += (ch>>8)&0xff; | |
if ( i ) { | |
ostr = base64_encode(ostr,prev); | |
prev_cnt=prev=0; | |
} else | |
len += 4; | |
prev = (ch&0xff); | |
prev_cnt=1; | |
} else { | |
prev<<=16; | |
prev |= ch; | |
if ( i ) { | |
ostr = base64_encode(ostr,prev); | |
prev_cnt=prev=0; | |
} else | |
len += 4; | |
prev_cnt = prev = 0; | |
} | |
} | |
if ( prev_cnt==2 ) { | |
prev<<=8; | |
if ( i ) { | |
ostr = base64_encode(ostr,prev); | |
prev_cnt=prev=0; | |
} else | |
len += 4; | |
} else if ( prev_cnt==1 ) { | |
prev<<=16; | |
if ( i ) { | |
ostr = base64_encode(ostr,prev); | |
prev_cnt=prev=0; | |
} else | |
len += 4; | |
} | |
if ( in ) { | |
if ( i ) | |
*ostr++ = '-'; | |
else | |
++len; | |
} | |
if ( i==0 ) | |
ostr = ret = malloc(len+1); | |
} | |
*ostr = '\0'; | |
return( ret ); | |
} | |
/* Long lines can be broken by inserting \\\n (backslash newline) */ | |
/* into the line. I don't think this is ever ambiguous as I don't */ | |
/* think a line can end with backslash */ | |
/* UPDATE: it can... that's handled in getquotedeol() below. */ | |
static int nlgetc(FILE *sfd) { | |
int ch, ch2; | |
ch=getc(sfd); | |
if ( ch!='\\' ) | |
return( ch ); | |
ch2 = getc(sfd); | |
if ( ch2=='\n' ) | |
return( nlgetc(sfd)); | |
ungetc(ch2,sfd); | |
return( ch ); | |
} | |
static char *SFDReadUTF7Str(FILE *sfd) { | |
char *buffer = NULL, *pt, *end = NULL; | |
int ch1, ch2, ch3, ch4, done, c; | |
int prev_cnt=0, prev=0, in=0; | |
ch1 = nlgetc(sfd); | |
while ( isspace(ch1) && ch1!='\n' && ch1!='\r') ch1 = nlgetc(sfd); | |
if ( ch1=='\n' || ch1=='\r' ) | |
ungetc(ch1,sfd); | |
if ( ch1!='"' ) | |
return( NULL ); | |
pt = 0; | |
while ( (ch1=nlgetc(sfd))!=EOF && ch1!='"' ) { | |
done = 0; | |
if ( !done && !in ) { | |
if ( ch1=='+' ) { | |
ch1 = nlgetc(sfd); | |
if ( ch1=='-' ) { | |
ch1 = '+'; | |
done = true; | |
} else { | |
in = true; | |
prev_cnt = 0; | |
} | |
} else | |
done = true; | |
} | |
if ( !done ) { | |
if ( ch1=='-' ) { | |
in = false; | |
} else if ( inbase64[ch1]==-1 ) { | |
in = false; | |
done = true; | |
} else { | |
ch1 = inbase64[ch1]; | |
ch2 = inbase64[c = nlgetc(sfd)]; | |
if ( ch2==-1 ) { | |
ungetc(c, sfd); | |
ch2 = ch3 = ch4 = 0; | |
} else { | |
ch3 = inbase64[c = nlgetc(sfd)]; | |
if ( ch3==-1 ) { | |
ungetc(c, sfd); | |
ch3 = ch4 = 0; | |
} else { | |
ch4 = inbase64[c = nlgetc(sfd)]; | |
if ( ch4==-1 ) { | |
ungetc(c, sfd); | |
ch4 = 0; | |
} | |
} | |
} | |
ch1 = (ch1<<18) | (ch2<<12) | (ch3<<6) | ch4; | |
if ( prev_cnt==0 ) { | |
prev = ch1&0xff; | |
ch1 >>= 8; | |
prev_cnt = 1; | |
} else /* if ( prev_cnt == 1 ) */ { | |
ch1 |= (prev<<24); | |
prev = (ch1&0xffff); | |
ch1 = (ch1>>16)&0xffff; | |
prev_cnt = 2; | |
} | |
done = true; | |
} | |
} | |
if ( pt+10>=end ) { | |
if ( buffer==NULL ) { | |
pt = buffer = malloc(400); | |
end = buffer+400; | |
} else if (pt) { | |
char *temp = realloc(buffer,end-buffer+400); | |
pt = temp+(pt-buffer); | |
end = temp+(end-buffer+400); | |
buffer = temp; | |
} | |
} | |
if ( pt && done ) | |
pt = utf8_idpb(pt,ch1,0); | |
if ( prev_cnt==2 ) { | |
prev_cnt = 0; | |
if ( pt && prev!=0 ) | |
pt = utf8_idpb(pt,prev,0); | |
} | |
if ( pt==0 ) { | |
free(buffer); | |
return( NULL ); | |
} | |
} | |
if ( buffer==NULL ) | |
return( NULL ); | |
*pt = '\0'; | |
pt = copy(buffer); | |
free(buffer ); | |
return( pt ); | |
} | |
char *utf7toutf8_copy(const char *_str) { | |
char *buffer = NULL, *pt, *end = NULL; | |
int ch1, ch2, ch3, ch4, done; | |
int prev_cnt=0, prev=0, in=0; | |
const char *str = _str; | |
if ( str==NULL ) | |
return( NULL ); | |
buffer = pt = malloc(400); | |
end = pt+400; | |
while ( (ch1=*str++)!='\0' ) { | |
done = 0; | |
if ( !done && !in ) { | |
if ( ch1=='+' ) { | |
ch1=*str++; | |
if ( ch1=='-' ) { | |
ch1 = '+'; | |
done = true; | |
} else { | |
in = true; | |
prev_cnt = 0; | |
} | |
} else | |
done = true; | |
} | |
if ( !done ) { | |
if ( ch1=='-' ) { | |
in = false; | |
} else if ( inbase64[ch1]==-1 ) { | |
in = false; | |
done = true; | |
} else { | |
ch1 = inbase64[ch1]; | |
ch2 = inbase64[(unsigned char) *str++]; | |
if ( ch2==1 ) { | |
--str; | |
ch2 = ch3 = ch4 = 0; | |
} else { | |
ch3 = inbase64[(unsigned char) *str++]; | |
if ( ch3==-1 ) { | |
--str; | |
ch3 = ch4 = 0; | |
} else { | |
ch4 = inbase64[(unsigned char) *str++]; | |
if ( ch4==-1 ) { | |
--str; | |
ch4 = 0; | |
} | |
} | |
} | |
ch1 = (ch1<<18) | (ch2<<12) | (ch3<<6) | ch4; | |
if ( prev_cnt==0 ) { | |
prev = ch1&0xff; | |
ch1 >>= 8; | |
prev_cnt = 1; | |
} else /* if ( prev_cnt == 1 ) */ { | |
ch1 |= (prev<<24); | |
prev = (ch1&0xffff); | |
ch1 = (ch1>>16)&0xffff; | |
prev_cnt = 2; | |
} | |
done = true; | |
} | |
} | |
if ( pt+10>=end ) { | |
char *temp = realloc(buffer,end-buffer+400); | |
pt = temp+(pt-buffer); | |
end = temp+(end-buffer+400); | |
buffer = temp; | |
} | |
if ( pt && done ) | |
pt = utf8_idpb(pt,ch1,0); | |
if ( prev_cnt==2 ) { | |
prev_cnt = 0; | |
if ( pt && prev!=0 ) | |
pt = utf8_idpb(pt,prev,0); | |
} | |
if ( pt==0 ) { | |
free(buffer); | |
return( NULL ); | |
} | |
} | |
*pt = '\0'; | |
pt = copy(buffer); | |
free(buffer ); | |
return( pt ); | |
} | |
struct enc85 { | |
FILE *sfd; | |
unsigned char sofar[4]; | |
int pos; | |
int ccnt; | |
}; | |
static void SFDEnc85(struct enc85 *enc,int ch) { | |
enc->sofar[enc->pos++] = ch; | |
if ( enc->pos==4 ) { | |
unsigned int val = (enc->sofar[0]<<24)|(enc->sofar[1]<<16)|(enc->sofar[2]<<8)|enc->sofar[3]; | |
if ( val==0 ) { | |
fputc('z',enc->sfd); | |
++enc->ccnt; | |
} else { | |
int ch2, ch3, ch4, ch5; | |
ch5 = val%85; | |
val /= 85; | |
ch4 = val%85; | |
val /= 85; | |
ch3 = val%85; | |
val /= 85; | |
ch2 = val%85; | |
val /= 85; | |
fputc('!'+val,enc->sfd); | |
fputc('!'+ch2,enc->sfd); | |
fputc('!'+ch3,enc->sfd); | |
fputc('!'+ch4,enc->sfd); | |
fputc('!'+ch5,enc->sfd); | |
enc->ccnt += 5; | |
if ( enc->ccnt > 70 ) { fputc('\n',enc->sfd); enc->ccnt=0; } | |
} | |
enc->pos = 0; | |
} | |
} | |
static void SFDEnc85EndEnc(struct enc85 *enc) { | |
int i; | |
int ch2, ch3, ch4, ch5; | |
unsigned val; | |
if ( enc->pos==0 ) | |
return; | |
for ( i=enc->pos; i<4; ++i ) | |
enc->sofar[i] = 0; | |
val = (enc->sofar[0]<<24)|(enc->sofar[1]<<16)|(enc->sofar[2]<<8)|enc->sofar[3]; | |
if ( val==0 ) { | |
fputc('z',enc->sfd); | |
} else { | |
ch5 = val%85; | |
val /= 85; | |
ch4 = val%85; | |
val /= 85; | |
ch3 = val%85; | |
val /= 85; | |
ch2 = val%85; | |
val /= 85; | |
fputc('!'+val,enc->sfd); | |
fputc('!'+ch2,enc->sfd); | |
fputc('!'+ch3,enc->sfd); | |
fputc('!'+ch4,enc->sfd); | |
fputc('!'+ch5,enc->sfd); | |
} | |
enc->pos = 0; | |
} | |
static void SFDDumpHintMask(FILE *sfd,HintMask *hintmask) { | |
unsigned i, j; | |
for ( i=HntMax/8-1; i>0; --i ) | |
if ( (*hintmask)[i]!=0 ) | |
break; | |
for ( j=0; /* j <= i, but that might never be true, so we test j == i at end of loop */ ; ++j ) { | |
if ( ((*hintmask)[j]>>4)<10 ) | |
putc('0'+((*hintmask)[j]>>4),sfd); | |
else | |
putc('a'-10+((*hintmask)[j]>>4),sfd); | |
if ( ((*hintmask)[j]&0xf)<10 ) | |
putc('0'+((*hintmask)[j]&0xf),sfd); | |
else | |
putc('a'-10+((*hintmask)[j]&0xf),sfd); | |
if (j == i) break; | |
} | |
} | |
static void SFDDumpSplineSet(FILE *sfd, SplineSet *spl, int want_order2) { | |
SplinePoint *first, *sp; | |
// If there's no spline structure there should just be a single point, | |
// which is compatible with either order and therefore want_order2 | |
int order2 = spl->first->next!=NULL ? spl->first->next->order2 : want_order2; | |
int reduce = (want_order2 && !order2); | |
if (order2 && !want_order2) | |
IError("Asked for cubic when had quadratic"); | |
SplineSet *nspl; | |
for ( ; spl!=NULL; spl=spl->next ) { | |
if (reduce) { | |
nspl = SSttfApprox(spl); | |
order2 = true; | |
} else { | |
nspl = spl; | |
} | |
first = NULL; | |
for ( sp = nspl->first; ; sp=sp->next->to ) { | |
#ifndef FONTFORGE_CONFIG_USE_DOUBLE | |
if ( first==NULL ) | |
fprintf( sfd, "%g %g m ", (double) sp->me.x, (double) sp->me.y ); | |
else if ( sp->prev->islinear && sp->noprevcp ) /* Don't use known linear here. save control points if there are any */ | |
fprintf( sfd, " %g %g l ", (double) sp->me.x, (double) sp->me.y ); | |
else | |
fprintf( sfd, " %g %g %g %g %g %g c ", | |
(double) sp->prev->from->nextcp.x, (double) sp->prev->from->nextcp.y, | |
(double) sp->prevcp.x, (double) sp->prevcp.y, | |
(double) sp->me.x, (double) sp->me.y ); | |
#else | |
if ( first==NULL ) | |
fprintf( sfd, "%.12g %.12g m ", (double) sp->me.x, (double) sp->me.y ); | |
else if ( sp->prev->islinear && sp->noprevcp ) /* Don't use known linear here. save control points if there are any */ | |
fprintf( sfd, " %.12g %.12g l ", (double) sp->me.x, (double) sp->me.y ); | |
else | |
fprintf( sfd, " %.12g %.12g %.12g %.12g %.12g %.12g c ", | |
(double) sp->prev->from->nextcp.x, (double) sp->prev->from->nextcp.y, | |
(double) sp->prevcp.x, (double) sp->prevcp.y, | |
(double) sp->me.x, (double) sp->me.y ); | |
#endif | |
int ptflags = 0; | |
ptflags = sp->pointtype|(sp->selected<<2)| | |
(sp->nextcpdef<<3)|(sp->prevcpdef<<4)| | |
(sp->roundx<<5)|(sp->roundy<<6)| | |
(sp->ttfindex==0xffff?(1<<7):0)| | |
(sp->dontinterpolate<<8)| | |
((sp->prev && sp->prev->acceptableextrema)<<9); | |
// Last point in the splineset, and we are an open path. | |
if( !sp->next | |
&& spl->first && !spl->first->prev ) | |
{ | |
ptflags |= SFD_PTFLAG_FORCE_OPEN_PATH; | |
} | |
fprintf(sfd, "%d", ptflags ); | |
if ( order2 ) { | |
if ( sp->ttfindex!=0xfffe && sp->nextcpindex!=0xfffe ) { | |
putc(',',sfd); | |
if ( sp->ttfindex==0xffff ) | |
fprintf(sfd,"-1"); | |
else if ( sp->ttfindex!=0xfffe ) | |
fprintf(sfd,"%d",sp->ttfindex); | |
if ( sp->nextcpindex==0xffff ) | |
fprintf(sfd,",-1"); | |
else if ( sp->nextcpindex!=0xfffe ) | |
fprintf(sfd,",%d",sp->nextcpindex); | |
} | |
} else { | |
if ( sp->hintmask!=NULL ) { | |
putc('x',sfd); | |
SFDDumpHintMask(sfd, sp->hintmask); | |
} | |
} | |
putc('\n',sfd); | |
if (sp->name != NULL) { | |
fputs("NamedP: ", sfd); | |
SFDDumpUTF7Str(sfd, sp->name); | |
putc('\n', sfd); | |
} | |
if ( sp==first ) | |
break; | |
if ( first==NULL ) first = sp; | |
if ( sp->next==NULL ) | |
break; | |
} | |
if ( spl->spiro_cnt!=0 ) { | |
int i; | |
fprintf( sfd, " Spiro\n" ); | |
for ( i=0; i<spl->spiro_cnt; ++i ) { | |
fprintf( sfd, " %g %g %c\n", spl->spiros[i].x, spl->spiros[i].y, | |
spl->spiros[i].ty&0x7f); | |
} | |
fprintf( sfd, " EndSpiro\n" ); | |
} | |
if ( spl->contour_name!=NULL ) { | |
fprintf( sfd, " Named: " ); | |
SFDDumpUTF7Str(sfd,spl->contour_name); | |
putc('\n',sfd); | |
} | |
if ( spl->is_clip_path ) { | |
fprintf( sfd, " PathFlags: %d\n", spl->is_clip_path ); | |
} | |
if ( spl->start_offset ) { | |
fprintf( sfd, " PathStart: %d\n", spl->start_offset ); | |
} | |
if (reduce) SplinePointListFree(nspl); | |
} | |
fprintf( sfd, "EndSplineSet\n" ); | |
} | |
static void SFDDumpDeviceTable(FILE *sfd,DeviceTable *adjust) { | |
int i; | |
if ( adjust==NULL ) | |
return; | |
fprintf( sfd, "{" ); | |
if ( adjust->corrections!=NULL ) { | |
fprintf( sfd, "%d-%d ", adjust->first_pixel_size, adjust->last_pixel_size ); | |
for ( i=0; i<=adjust->last_pixel_size-adjust->first_pixel_size; ++i ) | |
fprintf( sfd, "%s%d", i==0?"":",", adjust->corrections[i]); | |
} | |
fprintf( sfd, "}" ); | |
} | |
static void SFDDumpValDevTab(FILE *sfd,ValDevTab *adjust) { | |
if ( adjust==NULL ) | |
return; | |
if ( !( adjust->xadjust.corrections || adjust->yadjust.corrections || adjust->xadv.corrections || adjust->yadv.corrections ) ) | |
return; | |
fprintf( sfd, " [ddx=" ); SFDDumpDeviceTable(sfd,&adjust->xadjust); | |
fprintf( sfd, " ddy=" ); SFDDumpDeviceTable(sfd,&adjust->yadjust); | |
fprintf( sfd, " ddh=" ); SFDDumpDeviceTable(sfd,&adjust->xadv); | |
fprintf( sfd, " ddv=" ); SFDDumpDeviceTable(sfd,&adjust->yadv); | |
putc(']',sfd); | |
} | |
static void SFDDumpAnchorPoints(FILE *sfd,AnchorPoint *ap) { | |
if (ap==NULL) { | |
return; | |
} | |
for ( ; ap!=NULL; ap=ap->next ) | |
{ | |
fprintf( sfd, "AnchorPoint: " ); | |
SFDDumpUTF7Str(sfd,ap->anchor->name); | |
putc(' ',sfd); | |
fprintf( sfd, "%g %g %s %d", | |
(double) ap->me.x, (double) ap->me.y, | |
ap->type==at_centry ? "entry" : | |
ap->type==at_cexit ? "exit" : | |
ap->type==at_mark ? "mark" : | |
ap->type==at_basechar ? "basechar" : | |
ap->type==at_baselig ? "baselig" : "basemark", | |
ap->lig_index ); | |
if ( ap->xadjust.corrections!=NULL || ap->yadjust.corrections!=NULL ) { | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,&ap->xadjust); | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,&ap->yadjust); | |
} else | |
if ( ap->has_ttf_pt ) | |
fprintf( sfd, " %d", ap->ttf_pt_index ); | |
putc('\n',sfd); | |
} | |
} | |
/* Run length encoding */ | |
/* We always start with a background pixel(1), each line is a series of counts */ | |
/* we alternate background/foreground. If we can't represent an entire run */ | |
/* as one count, then we can split it up into several smaller runs and put */ | |
/* 0 counts in between */ | |
/* counts 0-254 mean 0-254 pixels of the current color */ | |
/* count 255 means that the next two bytes (bigendian) provide a two byte count */ | |
/* count 255 0 n (n<255) means that the previous line should be repeated n+1 times */ | |
/* count 255 0 255 means 255 pixels of the current color */ | |
static uint8_t *image2rle(struct _GImage *img, int *len) { | |
int max = img->height*img->bytes_per_line; | |
uint8_t *rle, *pt, *end; | |
int cnt, set; | |
int i,j,k; | |
*len = 0; | |
if ( img->image_type!=it_mono || img->bytes_per_line<5 ) | |
return( NULL ); | |
rle = calloc(max,sizeof(uint8_t)), pt = rle, end=rle+max-3; | |
for ( i=0; i<img->height; ++i ) { | |
if ( i!=0 ) { | |
if ( memcmp(img->data+i*img->bytes_per_line, | |
img->data+(i-1)*img->bytes_per_line, img->bytes_per_line)== 0 ) { | |
for ( k=1; k<img->height-i; ++k ) { | |
if ( memcmp(img->data+(i+k)*img->bytes_per_line, | |
img->data+i*img->bytes_per_line, img->bytes_per_line)!= 0 ) | |
break; | |
} | |
i+=k; | |
while ( k>0 ) { | |
if ( pt>end ) { | |
free(rle); | |
return( NULL ); | |
} | |
*pt++ = 255; | |
*pt++ = 0; | |
*pt++ = k>254 ? 254 : k; | |
k -= 254; | |
} | |
if ( i>=img->height ) | |
break; | |
} | |
} | |
set=1; cnt=0; j=0; | |
while ( j<img->width ) { | |
for ( k=j; k<img->width; ++k ) { | |
if (( set && !(img->data[i*img->bytes_per_line+(k>>3)]&(0x80>>(k&7))) ) || | |
( !set && (img->data[i*img->bytes_per_line+(k>>3)]&(0x80>>(k&7))) )) | |
break; | |
} | |
cnt = k-j; | |
j=k; | |
do { | |
if ( pt>=end ) { | |
free(rle); | |
return( NULL ); | |
} | |
if ( cnt<=254 ) | |
*pt++ = cnt; | |
else { | |
*pt++ = 255; | |
if ( cnt>65535 ) { | |
*pt++ = 255; | |
*pt++ = 255; | |
*pt++ = 0; /* nothing of the other color, we've still got more of this one */ | |
} else { | |
*pt++ = cnt>>8; | |
*pt++ = cnt&0xff; | |
} | |
} | |
cnt -= 65535; | |
} while ( cnt>0 ); | |
set = 1-set; | |
} | |
} | |
*len = pt-rle; | |
return( rle ); | |
} | |
void SFDDumpUndo(FILE *sfd,SplineChar *sc,Undoes *u, const char* keyPrefix, int idx ) { | |
fprintf(sfd, "%sOperation\n", keyPrefix ); | |
fprintf(sfd, "Index: %d\n", idx ); | |
fprintf(sfd, "Type: %d\n", u->undotype ); | |
fprintf(sfd, "WasModified: %d\n", u->was_modified ); | |
fprintf(sfd, "WasOrder2: %d\n", u->was_order2 ); | |
if( u->layer != UNDO_LAYER_UNKNOWN ) | |
{ | |
fprintf(sfd, "Layer: %d\n", u->layer ); | |
} | |
switch( u->undotype ) | |
{ | |
case ut_tstate: | |
case ut_state: | |
fprintf(sfd, "Width: %d\n", u->u.state.width ); | |
fprintf(sfd, "VWidth: %d\n", u->u.state.vwidth ); | |
fprintf(sfd, "LBearingChange: %d\n", u->u.state.lbearingchange ); | |
fprintf(sfd, "UnicodeEnc: %d\n", u->u.state.unicodeenc ); | |
if( u->u.state.charname ) | |
fprintf(sfd, "Charname: \"%s\"\n", u->u.state.charname ); | |
if( u->u.state.comment ) | |
fprintf(sfd, "Comment: \"%s\"\n", u->u.state.comment ); | |
if( u->u.state.refs ) { | |
SFDDumpRefs( sfd, u->u.state.refs, 0 ); | |
} | |
if( u->u.state.images ) { | |
#ifndef _NO_LIBPNG | |
if (WritePNGInSFD) | |
SFDDumpImagePNG( sfd, u->u.state.images ); | |
else | |
#endif | |
SFDDumpImage( sfd, u->u.state.images ); | |
} | |
fprintf(sfd, "InstructionsLength: %d\n", u->u.state.instrs_len ); | |
if( u->u.state.anchor ) { | |
SFDDumpAnchorPoints( sfd, u->u.state.anchor ); | |
} | |
if( u->u.state.splines ) { | |
fprintf(sfd, "SplineSet\n" ); | |
SFDDumpSplineSet( sfd, u->u.state.splines, u->was_order2 ); | |
} | |
break; | |
case ut_statehint: | |
{ | |
SplineChar* tsc = 0; | |
tsc = SplineCharCopy( sc, 0, 0 ); | |
ExtractHints( tsc, u->u.state.hints, 1 ); | |
SFDDumpHintList( sfd, "HStem: ", tsc->hstem); | |
SFDDumpHintList( sfd, "VStem: ", tsc->vstem); | |
SFDDumpDHintList( sfd, "DStem2: ", tsc->dstem); | |
SplineCharFree( tsc ); | |
if( u->u.state.instrs_len ) | |
SFDDumpTtfInstrsExplicit( sfd, u->u.state.instrs, u->u.state.instrs_len ); | |
break; | |
} | |
case ut_hints: | |
{ | |
SplineChar* tsc = 0; | |
tsc = SplineCharCopy( sc, 0, 0 ); | |
tsc->ttf_instrs = 0; | |
ExtractHints( tsc, u->u.state.hints, 1 ); | |
SFDDumpHintList( sfd, "HStem: ", tsc->hstem); | |
SFDDumpHintList( sfd, "VStem: ", tsc->vstem); | |
SFDDumpDHintList( sfd, "DStem2: ", tsc->dstem); | |
SplineCharFree( tsc ); | |
if( u->u.state.instrs_len ) | |
SFDDumpTtfInstrsExplicit( sfd, u->u.state.instrs, u->u.state.instrs_len ); | |
if( u->copied_from && u->copied_from->fullname ) | |
fprintf(sfd, "CopiedFrom: %s\n", u->copied_from->fullname ); | |
break; | |
} | |
case ut_width: | |
case ut_vwidth: | |
{ | |
fprintf(sfd, "Width: %d\n", u->u.width ); | |
break; | |
} | |
default: | |
break; | |
} | |
fprintf(sfd, "End%sOperation\n", keyPrefix ); | |
} | |
static void SFDDumpImage(FILE *sfd,ImageList *img) { | |
GImage *image = img->image; | |
struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0]; | |
struct enc85 enc; | |
int rlelen; | |
uint8_t *rle; | |
int i; | |
rle = image2rle(base,&rlelen); | |
fprintf(sfd, "Image: %d %d %d %d %d %x %g %g %g %g %d\n", | |
(int) base->width, (int) base->height, base->image_type, | |
(int) (base->image_type==it_true?3*base->width:base->bytes_per_line), | |
base->clut==NULL?0:base->clut->clut_len,(int) base->trans, | |
(double) img->xoff, (double) img->yoff, (double) img->xscale, (double) img->yscale, rlelen ); | |
memset(&enc,'\0',sizeof(enc)); | |
enc.sfd = sfd; | |
if ( base->clut!=NULL ) { | |
for ( i=0; i<base->clut->clut_len; ++i ) { | |
SFDEnc85(&enc,base->clut->clut[i]>>16); | |
SFDEnc85(&enc,(base->clut->clut[i]>>8)&0xff); | |
SFDEnc85(&enc,base->clut->clut[i]&0xff); | |
} | |
} | |
if ( rle!=NULL ) { | |
uint8_t *pt=rle, *end=rle+rlelen; | |
while ( pt<end ) | |
SFDEnc85(&enc,*pt++); | |
free( rle ); | |
} else { | |
for ( i=0; i<base->height; ++i ) { | |
if ( base->image_type==it_rgba ) { | |
uint32_t *ipt = (uint32_t *) (base->data + i*base->bytes_per_line); | |
uint32_t *iend = (uint32_t *) (base->data + (i+1)*base->bytes_per_line); | |
while ( ipt<iend ) { | |
SFDEnc85(&enc,*ipt>>24); | |
SFDEnc85(&enc,(*ipt>>16)&0xff); | |
SFDEnc85(&enc,(*ipt>>8)&0xff); | |
SFDEnc85(&enc,*ipt&0xff); | |
++ipt; | |
} | |
} else if ( base->image_type==it_true ) { | |
int *ipt = (int *) (base->data + i*base->bytes_per_line); | |
int *iend = (int *) (base->data + (i+1)*base->bytes_per_line); | |
while ( ipt<iend ) { | |
SFDEnc85(&enc,*ipt>>16); | |
SFDEnc85(&enc,(*ipt>>8)&0xff); | |
SFDEnc85(&enc,*ipt&0xff); | |
++ipt; | |
} | |
} else { | |
uint8_t *pt = (uint8_t *) (base->data + i*base->bytes_per_line); | |
uint8_t *end = (uint8_t *) (base->data + (i+1)*base->bytes_per_line); | |
while ( pt<end ) { | |
SFDEnc85(&enc,*pt); | |
++pt; | |
} | |
} | |
} | |
} | |
SFDEnc85EndEnc(&enc); | |
fprintf(sfd,"\nEndImage\n" ); | |
} | |
#ifndef _NO_LIBPNG | |
static void SFDDumpImagePNG(FILE *sfd,ImageList *img) { | |
struct enc85 enc = {0}; | |
char* pngbuf; | |
size_t pnglen, i; | |
if (!GImageWritePngBuf(img->image, &pngbuf, &pnglen, 1, false)) { | |
IError("Failed to serialise PNG image"); | |
return; | |
} | |
fprintf(sfd, "Image2: image/png %d %g %g %g %g\n", | |
(int)pnglen, (double) img->xoff, (double) img->yoff, (double) img->xscale, (double) img->yscale ); | |
enc.sfd = sfd; | |
for (i = 0; i<pnglen; ++i) { | |
SFDEnc85(&enc, pngbuf[i]); | |
} | |
free(pngbuf); | |
SFDEnc85EndEnc(&enc); | |
fprintf(sfd,"\nEndImage2\n" ); | |
} | |
#endif | |
static void SFDDumpHintList(FILE *sfd,const char *key, StemInfo *h) { | |
HintInstance *hi; | |
if ( h==NULL ) | |
return; | |
fprintf(sfd, "%s", key ); | |
for ( ; h!=NULL; h=h->next ) { | |
fprintf(sfd, "%g %g", (double) h->start,(double) h->width ); | |
if ( h->ghost ) putc('G',sfd); | |
if ( h->where!=NULL ) { | |
putc('<',sfd); | |
for ( hi=h->where; hi!=NULL; hi=hi->next ) | |
fprintf(sfd, "%g %g%c", (double) hi->begin, (double) hi->end, hi->next?' ':'>'); | |
} | |
putc(h->next?' ':'\n',sfd); | |
} | |
} | |
static void SFDDumpDHintList( FILE *sfd,const char *key, DStemInfo *d ) { | |
HintInstance *hi; | |
if ( d==NULL ) | |
return; | |
fprintf(sfd, "%s", key ); | |
for ( ; d!=NULL; d=d->next ) { | |
fprintf(sfd, "%g %g %g %g %g %g", | |
(double) d->left.x, (double) d->left.y, | |
(double) d->right.x, (double) d->right.y, | |
(double) d->unit.x, (double) d->unit.y ); | |
if ( d->where!=NULL ) { | |
putc('<',sfd); | |
for ( hi=d->where; hi!=NULL; hi=hi->next ) | |
fprintf(sfd, "%g %g%c", (double) hi->begin, (double) hi->end, hi->next?' ':'>'); | |
} | |
putc(d->next?' ':'\n',sfd); | |
} | |
} | |
static void SFDDumpTtfInstrsExplicit(FILE *sfd,uint8_t *ttf_instrs, int16_t ttf_instrs_len ) | |
{ | |
char *instrs = _IVUnParseInstrs( ttf_instrs, ttf_instrs_len ); | |
char *pt; | |
fprintf( sfd, "TtInstrs:\n" ); | |
for ( pt=instrs; *pt!='\0'; ++pt ) | |
putc(*pt,sfd); | |
if ( pt[-1]!='\n' ) | |
putc('\n',sfd); | |
free(instrs); | |
fprintf( sfd, "%s\n", end_tt_instrs ); | |
} | |
static void SFDDumpTtfInstrs(FILE *sfd,SplineChar *sc) | |
{ | |
SFDDumpTtfInstrsExplicit( sfd, sc->ttf_instrs,sc->ttf_instrs_len ); | |
} | |
static void SFDDumpTtfTable(FILE *sfd,struct ttf_table *tab,SplineFont *sf) { | |
if ( tab->tag == CHR('p','r','e','p') || tab->tag == CHR('f','p','g','m') ) { | |
/* These are tables of instructions and should be dumped as such */ | |
char *instrs; | |
char *pt; | |
fprintf( sfd, "TtTable: %c%c%c%c\n", | |
(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff) ); | |
instrs = _IVUnParseInstrs( tab->data,tab->len ); | |
for ( pt=instrs; *pt!='\0'; ++pt ) | |
putc(*pt,sfd); | |
if ( pt[-1]!='\n' ) | |
putc('\n',sfd); | |
free(instrs); | |
fprintf( sfd, "%s\n", end_tt_instrs ); | |
} else if ( (tab->tag == CHR('c','v','t',' ') || tab->tag == CHR('m','a','x','p')) && | |
(tab->len&1)==0 ) { | |
int i, ended; | |
uint8_t *pt; | |
fprintf( sfd, "ShortTable: %c%c%c%c %d\n", | |
(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff), | |
(int) (tab->len>>1) ); | |
pt = (uint8_t*) tab->data; | |
ended = tab->tag!=CHR('c','v','t',' ') || sf->cvt_names==NULL; | |
for ( i=0; i<(tab->len>>1); ++i ) { | |
int num = (int16_t) ((pt[0]<<8) | pt[1]); | |
fprintf( sfd, " %d", num ); | |
if ( !ended ) { | |
if ( sf->cvt_names[i]==END_CVT_NAMES ) | |
ended=true; | |
else if ( sf->cvt_names[i]!=NULL ) { | |
putc(' ',sfd); | |
SFDDumpUTF7Str(sfd,sf->cvt_names[i]); | |
putc(' ',sfd); | |
} | |
} | |
putc('\n',sfd); | |
pt += 2; | |
} | |
fprintf( sfd, "EndShort\n"); | |
} else { | |
/* maxp, who knows what. Dump 'em as binary for now */ | |
struct enc85 enc; | |
int i; | |
memset(&enc,'\0',sizeof(enc)); | |
enc.sfd = sfd; | |
fprintf( sfd, "TtfTable: %c%c%c%c %d\n", | |
(int) (tab->tag>>24), (int) ((tab->tag>>16)&0xff), (int) ((tab->tag>>8)&0xff), (int) (tab->tag&0xff), | |
(int) tab->len ); | |
for ( i=0; i<tab->len; ++i ) | |
SFDEnc85(&enc,tab->data[i]); | |
SFDEnc85EndEnc(&enc); | |
fprintf(sfd,"\nEndTtf\n" ); | |
} | |
} | |
static int SFDOmit(SplineChar *sc) { | |
int layer; | |
BDFFont *bdf; | |
if ( sc==NULL ) | |
return( true ); | |
if ( sc->comment!=NULL || sc->color!=COLOR_DEFAULT ) | |
return( false ); | |
for ( layer = ly_back; layer<sc->layer_cnt; ++layer ) { | |
if ( sc->layers[layer].splines!=NULL || | |
sc->layers[layer].refs!=NULL || | |
sc->layers[layer].images!=NULL ) | |
return( false ); | |
} | |
if ( sc->parent->onlybitmaps ) { | |
for ( bdf = sc->parent->bitmaps; bdf!=NULL; bdf=bdf->next ) { | |
if ( sc->orig_pos<bdf->glyphcnt && bdf->glyphs[sc->orig_pos]!=NULL ) | |
return( false ); | |
} | |
} | |
if ( !sc->widthset ) | |
return(true); | |
return( false ); | |
} | |
static void SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids) { | |
RefChar *ref; | |
for ( ref=refs; ref!=NULL; ref=ref->next ) if ( ref->sc!=NULL ) { | |
fprintf(sfd, "Refer: %d %d %c %g %g %g %g %g %g %d", | |
newgids!=NULL ? newgids[ref->sc->orig_pos]:ref->sc->orig_pos, | |
ref->sc->unicodeenc, | |
ref->selected?'S':'N', | |
(double) ref->transform[0], (double) ref->transform[1], (double) ref->transform[2], | |
(double) ref->transform[3], (double) ref->transform[4], (double) ref->transform[5], | |
ref->use_my_metrics|(ref->round_translation_to_grid<<1)| | |
(ref->point_match<<2)); | |
if ( ref->point_match ) { | |
fprintf(sfd, " %d %d", ref->match_pt_base, ref->match_pt_ref ); | |
if ( ref->point_match_out_of_date ) | |
fprintf( sfd, " O" ); | |
} | |
putc('\n',sfd); | |
} | |
} | |
static void SFDDumpGuidelines(FILE *sfd, GuidelineSet *gl) { | |
if (gl==NULL) { | |
return; | |
} | |
for ( ; gl!=NULL; gl=gl->next ) | |
{ | |
fprintf( sfd, "Guideline: " ); | |
SFDDumpUTF7Str(sfd,gl->name); | |
putc(' ',sfd); | |
SFDDumpUTF7Str(sfd,gl->identifier); | |
putc(' ',sfd); | |
fprintf( sfd, "%g %g %g %u %d", | |
(double) gl->point.x, (double) gl->point.y, | |
(double) gl->angle, gl->color, gl->flags); | |
putc('\n',sfd); | |
} | |
} | |
static void SFDDumpMathVertex(FILE *sfd,struct mathkernvertex *vert,const char *name) { | |
int i; | |
if ( vert==NULL || vert->cnt==0 ) | |
return; | |
fprintf( sfd, "%s %d ", name, vert->cnt ); | |
for ( i=0; i<vert->cnt; ++i ) { | |
fprintf( sfd, " %d", vert->mkd[i].height ); | |
SFDDumpDeviceTable(sfd,vert->mkd[i].height_adjusts ); | |
fprintf( sfd, ",%d", vert->mkd[i].kern ); | |
SFDDumpDeviceTable(sfd,vert->mkd[i].kern_adjusts ); | |
} | |
putc('\n',sfd ); | |
} | |
static void SFDDumpGlyphVariants(FILE *sfd,struct glyphvariants *gv,const char *name) { | |
int i; | |
if ( gv==NULL ) | |
return; | |
if ( gv->variants!=NULL ) | |
fprintf( sfd, "GlyphVariants%s: %s\n", name, gv->variants ); | |
if ( gv->part_cnt!=0 ) { | |
if ( gv->italic_correction!=0 ) { | |
fprintf( sfd, "GlyphComposition%sIC: %d", name, gv->italic_correction ); | |
if ( gv->italic_adjusts!=NULL ) { | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,gv->italic_adjusts); | |
} | |
putc('\n',sfd); | |
} | |
fprintf( sfd, "GlyphComposition%s: %d ", name, gv->part_cnt ); | |
for ( i=0; i<gv->part_cnt; ++i ) { | |
fprintf( sfd, " %s%%%d,%d,%d,%d", gv->parts[i].component, | |
gv->parts[i].is_extender, | |
gv->parts[i].startConnectorLength, | |
gv->parts[i].endConnectorLength, | |
gv->parts[i].fullAdvance); | |
} | |
putc('\n',sfd); | |
} | |
} | |
static void SFDDumpCharMath(FILE *sfd,SplineChar *sc) { | |
if ( sc->italic_correction!=TEX_UNDEF && sc->italic_correction!=0 ) { | |
fprintf( sfd, "ItalicCorrection: %d", sc->italic_correction ); | |
if ( sc->italic_adjusts!=NULL ) { | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,sc->italic_adjusts); | |
} | |
putc('\n',sfd); | |
} | |
if ( sc->top_accent_horiz!=TEX_UNDEF ) { | |
fprintf( sfd, "TopAccentHorizontal: %d", sc->top_accent_horiz ); | |
if ( sc->top_accent_adjusts!=NULL ) { | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,sc->top_accent_adjusts); | |
} | |
putc('\n',sfd); | |
} | |
if ( sc->is_extended_shape ) | |
fprintf( sfd, "IsExtendedShape: %d\n", sc->is_extended_shape ); | |
SFDDumpGlyphVariants(sfd,sc->vert_variants,"Vertical"); | |
SFDDumpGlyphVariants(sfd,sc->horiz_variants,"Horizontal"); | |
if ( sc->mathkern!=NULL ) { | |
SFDDumpMathVertex(sfd,&sc->mathkern->top_right,"TopRightVertex:"); | |
SFDDumpMathVertex(sfd,&sc->mathkern->top_left,"TopLeftVertex:"); | |
SFDDumpMathVertex(sfd,&sc->mathkern->bottom_right,"BottomRightVertex:"); | |
SFDDumpMathVertex(sfd,&sc->mathkern->bottom_left,"BottomLeftVertex:"); | |
} | |
} | |
static void SFDPickleMe(FILE *sfd,void *python_data, int python_data_has_lists) { | |
char *string, *pt; | |
#ifdef _NO_PYTHON | |
string = (char *) python_data; | |
#else | |
string = PyFF_PickleMeToString(python_data); | |
#endif | |
if ( string==NULL ) | |
return; | |
if (python_data_has_lists) | |
fprintf( sfd, "PickledDataWithLists: \"" ); | |
else | |
fprintf( sfd, "PickledData: \"" ); | |
for ( pt=string; *pt; ++pt ) { | |
if ( *pt=='\\' || *pt=='"' ) | |
putc('\\',sfd); | |
putc(*pt,sfd); | |
} | |
fprintf( sfd, "\"\n"); | |
#ifndef _NO_PYTHON | |
free(string); | |
#endif | |
} | |
static void *SFDUnPickle(FILE *sfd, int python_data_has_lists) { | |
int ch, quoted; | |
static int max = 0; | |
static char *buf = NULL; | |
char *pt, *end; | |
int cnt; | |
pt = buf; end = buf+max; | |
while ( (ch=nlgetc(sfd))!='"' && ch!='\n' && ch!=EOF ); | |
if ( ch!='"' ) | |
return( NULL ); | |
quoted = false; | |
while ( ((ch=nlgetc(sfd))!='"' || quoted) && ch!=EOF ) { | |
if ( !quoted && ch=='\\' ) | |
quoted = true; | |
else { | |
if ( pt>=end ) { | |
cnt = pt-buf; | |
buf = realloc(buf,(max+=200)+1); | |
pt = buf+cnt; | |
end = buf+max; | |
} | |
*pt++ = ch; | |
quoted = false; | |
} | |
} | |
if ( pt==buf ) | |
return( NULL ); | |
*pt='\0'; | |
#ifdef _NO_PYTHON | |
return( copy(buf)); | |
#else | |
return( PyFF_UnPickleMeToObjects(buf)); | |
#endif | |
/* buf is a static buffer, I don't free it, I'll reuse it next time */ | |
} | |
static void SFDDumpGradient(FILE *sfd, const char *keyword, struct gradient *gradient) { | |
int i; | |
/* Use ";" as a coord separator because we treat "," as a potential decimal point */ | |
fprintf( sfd, "%s %g;%g %g;%g %g %s %d ", keyword, | |
(double) gradient->start.x, (double) gradient->start.y, | |
(double) gradient->stop.x, (double) gradient->stop.y, | |
(double) gradient->radius, | |
spreads[gradient->sm], | |
gradient->stop_cnt ); | |
for ( i=0 ; i<gradient->stop_cnt; ++i ) { | |
fprintf( sfd, "{%g #%06x %g} ", (double) gradient->grad_stops[i].offset, | |
gradient->grad_stops[i].col, (double) gradient->grad_stops[i].opacity ); | |
} | |
putc('\n',sfd); | |
} | |
static void SFDDumpPattern(FILE *sfd, const char *keyword, struct pattern *pattern) { | |
fprintf( sfd, "%s %s %g;%g [%g %g %g %g %g %g]\n", keyword, | |
pattern->pattern, | |
(double) pattern->width, (double) pattern->height, | |
(double) pattern->transform[0], (double) pattern->transform[1], | |
(double) pattern->transform[2], (double) pattern->transform[3], | |
(double) pattern->transform[4], (double) pattern->transform[5] ); | |
} | |
void SFD_DumpPST( FILE *sfd, SplineChar *sc ) { | |
PST *pst; | |
for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { | |
if (( pst->subtable==NULL && pst->type!=pst_lcaret) || pst->type==pst_null ) | |
/* Skip it */; | |
else { | |
static const char *keywords[] = { "Null:", "Position2:", "PairPos2:", | |
"Substitution2:", | |
"AlternateSubs2:", "MultipleSubs2:", "Ligature2:", | |
"LCarets2:", NULL }; | |
fprintf( sfd, "%s ", keywords[pst->type] ); | |
if ( pst->subtable!=NULL ) { | |
SFDDumpUTF7Str(sfd,pst->subtable->subtable_name); | |
putc(' ',sfd); | |
} | |
if ( pst->type==pst_position ) { | |
fprintf( sfd, "dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pos.xoff, pst->u.pos.yoff, | |
pst->u.pos.h_adv_off, pst->u.pos.v_adv_off); | |
SFDDumpValDevTab(sfd,pst->u.pos.adjust); | |
putc('\n',sfd); | |
} else if ( pst->type==pst_pair ) { | |
fprintf( sfd, "%s dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pair.paired, | |
pst->u.pair.vr[0].xoff, pst->u.pair.vr[0].yoff, | |
pst->u.pair.vr[0].h_adv_off, pst->u.pair.vr[0].v_adv_off ); | |
SFDDumpValDevTab(sfd,pst->u.pair.vr[0].adjust); | |
fprintf( sfd, " dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pair.vr[1].xoff, pst->u.pair.vr[1].yoff, | |
pst->u.pair.vr[1].h_adv_off, pst->u.pair.vr[1].v_adv_off); | |
SFDDumpValDevTab(sfd,pst->u.pair.vr[1].adjust); | |
putc('\n',sfd); | |
} else if ( pst->type==pst_lcaret ) { | |
int i; | |
fprintf( sfd, "%d ", pst->u.lcaret.cnt ); | |
for ( i=0; i<pst->u.lcaret.cnt; ++i ) { | |
fprintf( sfd, "%d", pst->u.lcaret.carets[i] ); | |
if ( i<pst->u.lcaret.cnt-1 ) putc(' ',sfd); | |
} | |
fprintf( sfd, "\n" ); | |
} else | |
fprintf( sfd, "%s\n", pst->u.lig.components ); | |
} | |
} | |
} | |
void SFD_DumpKerns( FILE *sfd, SplineChar *sc, int *newgids ) { | |
KernPair *kp; | |
int v; | |
for ( v=0; v<2; ++v ) { | |
kp = v ? sc->vkerns : sc->kerns; | |
if ( kp!=NULL ) { | |
fprintf( sfd, v ? "VKerns2:" : "Kerns2:" ); | |
for ( ; kp!=NULL; kp=kp->next ) | |
if ( !SFDOmit(kp->sc)) { | |
fprintf( sfd, " %d %d ", | |
newgids!=NULL?newgids[kp->sc->orig_pos]:kp->sc->orig_pos, | |
kp->off ); | |
SFDDumpUTF7Str(sfd,kp->subtable->subtable_name); | |
if ( kp->adjust!=NULL ) putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,kp->adjust); | |
} | |
fprintf(sfd, "\n" ); | |
} | |
} | |
} | |
void SFDDumpCharStartingMarker(FILE *sfd,SplineChar *sc) { | |
if ( AllAscii(sc->name)) | |
fprintf(sfd, "StartChar: %s\n", sc->name ); | |
else { | |
fprintf(sfd, "StartChar: " ); | |
SFDDumpUTF7Str(sfd,sc->name); | |
putc('\n',sfd); | |
} | |
} | |
static void SFDDumpChar(FILE *sfd,SplineChar *sc,EncMap *map,int *newgids,int todir,int saveUndoes) { | |
// TODO: Output the U. F. O. glif name. | |
ImageList *img; | |
KernPair *kp; | |
PST *pst; | |
int i, v, enc; | |
struct altuni *altuni; | |
if (!todir) | |
putc('\n',sfd); | |
SFDDumpCharStartingMarker( sfd, sc ); | |
if ( (enc = map->backmap[sc->orig_pos])>=map->enccount ) { | |
if ( sc->parent->cidmaster==NULL ) | |
IError("Bad reverse encoding"); | |
enc = -1; | |
} | |
if ( sc->unicodeenc!=-1 && | |
((map->enc->is_unicodebmp && sc->unicodeenc<0x10000) || | |
(map->enc->is_unicodefull && sc->unicodeenc < (int)unicode4_size)) ) | |
/* If we have altunis, then the backmap may not give the primary */ | |
/* unicode code point, which is what we need here */ | |
fprintf(sfd, "Encoding: %d %d %d\n", sc->unicodeenc, sc->unicodeenc, | |
newgids!=NULL?newgids[sc->orig_pos]:sc->orig_pos); | |
else | |
fprintf(sfd, "Encoding: %d %d %d\n", enc, sc->unicodeenc, | |
newgids!=NULL?newgids[sc->orig_pos]:sc->orig_pos); | |
if ( sc->altuni ) { | |
fprintf( sfd, "AltUni2:" ); | |
for ( altuni = sc->altuni; altuni!=NULL; altuni=altuni->next ) | |
fprintf( sfd, " %06x.%06x.%x", altuni->unienc, altuni->vs, altuni->fid ); | |
putc( '\n', sfd); | |
} | |
if ( sc->glif_name ) { | |
fprintf(sfd, "GlifName: "); | |
if ( AllAscii(sc->glif_name)) | |
fprintf(sfd, "%s", sc->glif_name ); | |
else { | |
SFDDumpUTF7Str(sfd,sc->glif_name); | |
} | |
putc('\n',sfd); | |
} | |
fprintf(sfd, "Width: %d\n", sc->width ); | |
if ( sc->vwidth!=sc->parent->ascent+sc->parent->descent ) | |
fprintf(sfd, "VWidth: %d\n", sc->vwidth ); | |
if ( sc->glyph_class!=0 ) | |
fprintf(sfd, "GlyphClass: %d\n", sc->glyph_class ); | |
if ( sc->unlink_rm_ovrlp_save_undo ) | |
fprintf(sfd, "UnlinkRmOvrlpSave: %d\n", sc->unlink_rm_ovrlp_save_undo ); | |
if ( sc->inspiro ) | |
fprintf(sfd, "InSpiro: %d\n", sc->inspiro ); | |
if ( sc->lig_caret_cnt_fixed ) | |
fprintf(sfd, "LigCaretCntFixed: %d\n", sc->lig_caret_cnt_fixed ); | |
if ( sc->changedsincelasthinted|| sc->manualhints || sc->widthset ) | |
fprintf(sfd, "Flags: %s%s%s%s%s\n", | |
sc->changedsincelasthinted?"H":"", | |
sc->manualhints?"M":"", | |
sc->widthset?"W":"", | |
sc->views!=NULL?"O":"", | |
sc->instructions_out_of_date?"I":""); | |
if ( sc->tex_height!=TEX_UNDEF || sc->tex_depth!=TEX_UNDEF ) | |
fprintf( sfd, "TeX: %d %d\n", sc->tex_height, sc->tex_depth ); | |
if ( sc->is_extended_shape || sc->italic_correction!=TEX_UNDEF || | |
sc->top_accent_horiz!=TEX_UNDEF || sc->vert_variants!=NULL || | |
sc->horiz_variants!=NULL || sc->mathkern!=NULL ) | |
SFDDumpCharMath(sfd,sc); | |
#if HANYANG | |
if ( sc->compositionunit ) | |
fprintf( sfd, "CompositionUnit: %d %d\n", sc->jamo, sc->variant ); | |
#endif | |
SFDDumpHintList(sfd,"HStem: ", sc->hstem); | |
SFDDumpHintList(sfd,"VStem: ", sc->vstem); | |
SFDDumpDHintList(sfd,"DStem2: ", sc->dstem); | |
if ( sc->countermask_cnt!=0 ) { | |
fprintf( sfd, "CounterMasks: %d", sc->countermask_cnt ); | |
for ( i=0; i<sc->countermask_cnt; ++i ) { | |
putc(' ',sfd); | |
SFDDumpHintMask(sfd,&sc->countermasks[i]); | |
} | |
putc('\n',sfd); | |
} | |
if ( sc->ttf_instrs_len!=0 ) | |
SFDDumpTtfInstrs(sfd,sc); | |
SFDDumpAnchorPoints(sfd,sc->anchor); | |
fprintf( sfd, "LayerCount: %d\n", sc->layer_cnt ); | |
for ( i=0; i<sc->layer_cnt; ++i ) { | |
if( saveUndoes && UndoRedoLimitToSave > 0) { | |
if( sc->layers[i].undoes || sc->layers[i].redoes ) { | |
fprintf(sfd, "UndoRedoHistory\n" ); | |
fprintf(sfd, "Layer: %d\n", i ); | |
Undoes *undo = 0; | |
int idx = 0; | |
int limit = 0; | |
fprintf(sfd, "Undoes\n" ); | |
idx = 0; | |
undo = sc->layers[i].undoes; | |
for( limit = UndoRedoLimitToSave; | |
undo && (limit==-1 || limit>0); | |
undo = undo->next, idx++ ) { | |
SFDDumpUndo( sfd, sc, undo, "Undo", idx ); | |
if( limit > 0 ) | |
limit--; | |
} | |
fprintf(sfd, "EndUndoes\n" ); | |
fprintf(sfd, "Redoes\n" ); | |
idx = 0; | |
limit = UndoRedoLimitToSave; | |
undo = sc->layers[i].redoes; | |
for( limit = UndoRedoLimitToSave; | |
undo && (limit==-1 || limit>0); | |
undo = undo->next, idx++ ) { | |
SFDDumpUndo( sfd, sc, undo, "Redo", idx ); | |
if( limit > 0 ) | |
limit--; | |
} | |
fprintf(sfd, "EndRedoes\n" ); | |
fprintf(sfd, "EndUndoRedoHistory\n" ); | |
} | |
} | |
if ( sc->parent->multilayer ) { | |
fprintf(sfd, "Layer: %d %d %d %d #%06x %g #%06x %g %g %s %s [%g %g %g %g] [", | |
i, sc->layers[i].dofill, sc->layers[i].dostroke, sc->layers[i].fillfirst, | |
sc->layers[i].fill_brush.col, (double) sc->layers[i].fill_brush.opacity, | |
sc->layers[i].stroke_pen.brush.col, (double) sc->layers[i].stroke_pen.brush.opacity, | |
(double) sc->layers[i].stroke_pen.width, joins[sc->layers[i].stroke_pen.linejoin], caps[sc->layers[i].stroke_pen.linecap], | |
(double) sc->layers[i].stroke_pen.trans[0], (double) sc->layers[i].stroke_pen.trans[1], | |
(double) sc->layers[i].stroke_pen.trans[2], (double) sc->layers[i].stroke_pen.trans[3] ); | |
if ( sc->layers[i].stroke_pen.dashes[0]==0 && sc->layers[i].stroke_pen.dashes[1]==DASH_INHERITED ) | |
fprintf(sfd,"0 %d]\n", DASH_INHERITED); | |
else { int j; | |
for ( j=0; j<DASH_MAX && sc->layers[i].stroke_pen.dashes[j]!=0; ++j ) | |
fprintf( sfd,"%d ", sc->layers[i].stroke_pen.dashes[j]); | |
fprintf(sfd,"]\n"); | |
} | |
if ( sc->layers[i].fill_brush.gradient!=NULL ) | |
SFDDumpGradient(sfd,"FillGradient:", sc->layers[i].fill_brush.gradient ); | |
else if ( sc->layers[i].fill_brush.pattern!=NULL ) | |
SFDDumpPattern(sfd,"FillPattern:", sc->layers[i].fill_brush.pattern ); | |
if ( sc->layers[i].stroke_pen.brush.gradient!=NULL ) | |
SFDDumpGradient(sfd,"StrokeGradient:", sc->layers[i].stroke_pen.brush.gradient ); | |
else if ( sc->layers[i].stroke_pen.brush.pattern!=NULL ) | |
SFDDumpPattern(sfd,"StrokePattern:", sc->layers[i].stroke_pen.brush.pattern ); | |
} else { | |
if ( sc->layers[i].images==NULL && sc->layers[i].splines==NULL && | |
sc->layers[i].refs==NULL && (sc->layers[i].validation_state&vs_known) == 0 && | |
sc->layers[i].python_persistent == NULL) | |
continue; | |
if ( i==ly_back ) | |
fprintf( sfd, "Back\n" ); | |
else if ( i==ly_fore ) | |
fprintf( sfd, "Fore\n" ); | |
else | |
fprintf(sfd, "Layer: %d\n", i ); | |
} | |
for ( img=sc->layers[i].images; img!=NULL; img=img->next ) | |
#ifndef _NO_LIBPNG | |
if (WritePNGInSFD) | |
SFDDumpImagePNG(sfd,img); | |
else | |
#endif | |
SFDDumpImage(sfd,img); | |
if ( sc->layers[i].splines!=NULL ) { | |
fprintf(sfd, "SplineSet\n" ); | |
SFDDumpSplineSet(sfd,sc->layers[i].splines,sc->layers[i].order2); | |
} | |
SFDDumpRefs(sfd,sc->layers[i].refs,newgids); | |
SFDDumpGuidelines(sfd, sc->layers[i].guidelines); | |
if ( sc->layers[i].validation_state&vs_known ) | |
fprintf( sfd, "Validated: %d\n", sc->layers[i].validation_state ); | |
if ( sc->layers[i].python_persistent!=NULL ) | |
SFDPickleMe(sfd,sc->layers[i].python_persistent,sc->layers[i].python_persistent_has_lists); | |
} | |
for ( v=0; v<2; ++v ) { | |
kp = v ? sc->vkerns : sc->kerns; | |
if ( kp!=NULL ) { | |
fprintf( sfd, v ? "VKerns2:" : "Kerns2:" ); | |
for ( ; kp!=NULL; kp=kp->next ) { | |
if ( !SFDOmit(kp->sc)) { | |
fprintf( sfd, " %d %d ", | |
newgids!=NULL?newgids[kp->sc->orig_pos]:kp->sc->orig_pos, | |
kp->off ); | |
SFDDumpUTF7Str(sfd,kp->subtable->subtable_name); | |
if ( kp->adjust!=NULL ) putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,kp->adjust); | |
} | |
} | |
fprintf(sfd, "\n" ); | |
} | |
} | |
for ( pst=sc->possub; pst!=NULL; pst=pst->next ) { | |
if (( pst->subtable==NULL && pst->type!=pst_lcaret) || pst->type==pst_null ) | |
/* Skip it */; | |
else { | |
static const char *keywords[] = { "Null:", "Position2:", "PairPos2:", | |
"Substitution2:", | |
"AlternateSubs2:", "MultipleSubs2:", "Ligature2:", | |
"LCarets2:", NULL }; | |
fprintf( sfd, "%s ", keywords[pst->type] ); | |
if ( pst->subtable!=NULL ) { | |
SFDDumpUTF7Str(sfd,pst->subtable->subtable_name); | |
putc(' ',sfd); | |
} | |
if ( pst->type==pst_position ) { | |
fprintf( sfd, "dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pos.xoff, pst->u.pos.yoff, | |
pst->u.pos.h_adv_off, pst->u.pos.v_adv_off); | |
SFDDumpValDevTab(sfd,pst->u.pos.adjust); | |
putc('\n',sfd); | |
} else if ( pst->type==pst_pair ) { | |
fprintf( sfd, "%s dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pair.paired, | |
pst->u.pair.vr[0].xoff, pst->u.pair.vr[0].yoff, | |
pst->u.pair.vr[0].h_adv_off, pst->u.pair.vr[0].v_adv_off ); | |
SFDDumpValDevTab(sfd,pst->u.pair.vr[0].adjust); | |
fprintf( sfd, " dx=%d dy=%d dh=%d dv=%d", | |
pst->u.pair.vr[1].xoff, pst->u.pair.vr[1].yoff, | |
pst->u.pair.vr[1].h_adv_off, pst->u.pair.vr[1].v_adv_off); | |
SFDDumpValDevTab(sfd,pst->u.pair.vr[1].adjust); | |
putc('\n',sfd); | |
} else if ( pst->type==pst_lcaret ) { | |
int i; | |
fprintf( sfd, "%d ", pst->u.lcaret.cnt ); | |
for ( i=0; i<pst->u.lcaret.cnt; ++i ) { | |
fprintf( sfd, "%d", pst->u.lcaret.carets[i] ); | |
if ( i<pst->u.lcaret.cnt-1 ) putc(' ',sfd); | |
} | |
fprintf( sfd, "\n" ); | |
} else | |
fprintf( sfd, "%s\n", pst->u.lig.components ); | |
} | |
} | |
if ( sc->comment!=NULL ) { | |
fprintf( sfd, "Comment: " ); | |
SFDDumpUTF7Str(sfd,sc->comment); | |
putc('\n',sfd); | |
} | |
if ( sc->user_decomp != NULL ) { | |
fprintf( sfd, "Decomposition: " ); | |
char* temp_ud = u2utf8_copy(sc->user_decomp); | |
SFDDumpUTF7Str(sfd, temp_ud); | |
free(temp_ud); | |
putc('\n',sfd); | |
} | |
if ( sc->color!=COLOR_DEFAULT ) | |
fprintf( sfd, "Colour: %x\n", (int) sc->color ); | |
if ( sc->parent->multilayer ) { | |
if ( sc->tile_margin!=0 ) | |
fprintf( sfd, "TileMargin: %g\n", (double) sc->tile_margin ); | |
else if ( sc->tile_bounds.minx!=0 || sc->tile_bounds.maxx!=0 ) | |
fprintf( sfd, "TileBounds: %g %g %g %g\n", (double) sc->tile_bounds.minx, (double) sc->tile_bounds.miny, (double) sc->tile_bounds.maxx, (double) sc->tile_bounds.maxy ); | |
} | |
fprintf(sfd,"EndChar\n" ); | |
} | |
static void SFDDumpBitmapChar(FILE *sfd,BDFChar *bfc, int enc,int *newgids) { | |
struct enc85 encrypt; | |
int i; | |
fprintf(sfd, "BDFChar: %d %d %d %d %d %d %d", | |
newgids!=NULL ? newgids[bfc->orig_pos] : bfc->orig_pos, enc, | |
bfc->width, bfc->xmin, bfc->xmax, bfc->ymin, bfc->ymax ); | |
if ( bfc->sc->parent->hasvmetrics ) | |
fprintf(sfd, " %d", bfc->vwidth); | |
putc('\n',sfd); | |
memset(&encrypt,'\0',sizeof(encrypt)); | |
encrypt.sfd = sfd; | |
for ( i=0; i<=bfc->ymax-bfc->ymin; ++i ) { | |
uint8_t *pt = (uint8_t *) (bfc->bitmap + i*bfc->bytes_per_line); | |
uint8_t *end = pt + bfc->bytes_per_line; | |
while ( pt<end ) { | |
SFDEnc85(&encrypt,*pt); | |
++pt; | |
} | |
} | |
SFDEnc85EndEnc(&encrypt); | |
fputc('\n',sfd); | |
} | |
static void appendnames(char *dest,char *dir,const char *dir_char,char *name,const char *ext ) { | |
strcpy(dest,dir); | |
dest += strlen(dest); | |
strcpy(dest,dir_char); | |
dest += strlen(dest); | |
/* Some file systems are case-insensitive, so we can't just */ | |
/* copy the glyph name blindly (else "A" and "a" would map to the same file */ | |
for (;;) { | |
if ( strncmp(name,"uni",3)==0 && ishexdigit(name[3]) && ishexdigit(name[4]) && | |
ishexdigit(name[5]) && ishexdigit(name[6])) { | |
/* but in a name like uni00AD case is irrelevant. Even under unix its */ | |
/* the same as uni00ad -- and it looks ugly */ | |
strncpy(dest,name,7); | |
dest += 7; name += 7; | |
while ( ishexdigit(name[0]) && ishexdigit(name[1]) && | |
ishexdigit(name[2]) && ishexdigit(name[3]) ) { | |
strncpy(dest,name,4); | |
dest += 4; name += 4; | |
} | |
} else if ( name[0]=='u' && ishexdigit(name[1]) && ishexdigit(name[2]) && | |
ishexdigit(name[3]) && ishexdigit(name[4]) && | |
ishexdigit(name[5]) ) { | |
strncpy(dest,name,5); | |
dest += 5; name += 5; | |
} else | |
break; | |
if ( *name!='_' ) | |
break; | |
*dest++ = '_'; | |
++name; | |
} | |
while ( *name ) { | |
if ( isupper(*name)) { | |
*dest++ = '_'; | |
*dest++ = *name; | |
} else | |
*dest++ = *name; | |
++name; | |
} | |
strcpy(dest,ext); | |
} | |
static int SFDDumpBitmapFont(FILE *sfd,BDFFont *bdf,EncMap *encm,int *newgids, | |
int todir, char *dirname) { | |
int i; | |
int err = false; | |
BDFChar *bc; | |
BDFRefChar *ref; | |
ff_progress_next_stage(); | |
if (bdf->foundry) | |
fprintf( sfd, "BitmapFont: %d %d %d %d %d %s\n", bdf->pixelsize, bdf->glyphcnt, | |
bdf->ascent, bdf->descent, BDFDepth(bdf), bdf->foundry ); | |
else | |
fprintf( sfd, "BitmapFont: %d %d %d %d %d\n", bdf->pixelsize, bdf->glyphcnt, | |
bdf->ascent, bdf->descent, BDFDepth(bdf) ); | |
if ( bdf->prop_cnt>0 ) { | |
fprintf( sfd, "BDFStartProperties: %d\n", bdf->prop_cnt ); | |
for ( i=0; i<bdf->prop_cnt; ++i ) { | |
fprintf(sfd,"%s %d ", bdf->props[i].name, bdf->props[i].type ); | |
switch ( bdf->props[i].type&~prt_property ) { | |
case prt_int: case prt_uint: | |
fprintf(sfd, "%d\n", bdf->props[i].u.val ); | |
break; | |
case prt_string: case prt_atom: | |
fprintf(sfd, "\"%s\"\n", bdf->props[i].u.str ); | |
break; | |
default: | |
break; | |
} | |
} | |
fprintf( sfd, "BDFEndProperties\n" ); | |
} | |
if ( bdf->res>20 ) | |
fprintf( sfd, "Resolution: %d\n", bdf->res ); | |
for ( i=0; i<bdf->glyphcnt; ++i ) { | |
if ( bdf->glyphs[i]!=NULL ) { | |
if ( todir ) { | |
char *glyphfile = malloc(strlen(dirname)+2*strlen(bdf->glyphs[i]->sc->name)+20); | |
FILE *gsfd; | |
appendnames(glyphfile,dirname,"/",bdf->glyphs[i]->sc->name,BITMAP_EXT ); | |
gsfd = fopen(glyphfile,"w"); | |
if ( gsfd!=NULL ) { | |
SFDDumpBitmapChar(gsfd,bdf->glyphs[i],encm->backmap[i],newgids); | |
if ( ferror(gsfd)) err = true; | |
if ( fclose(gsfd)) err = true; | |
} else | |
err = true; | |
free(glyphfile); | |
} else | |
SFDDumpBitmapChar(sfd,bdf->glyphs[i],encm->backmap[i],newgids); | |
} | |
ff_progress_next(); | |
} | |
for ( i=0; i<bdf->glyphcnt; ++i ) if (( bc = bdf->glyphs[i] ) != NULL ) { | |
for ( ref=bc->refs; ref!=NULL; ref=ref->next ) | |
fprintf(sfd, "BDFRefChar: %d %d %d %d %c\n", | |
newgids!=NULL ? newgids[bc->orig_pos] : bc->orig_pos, | |
newgids!=NULL ? newgids[ref->bdfc->orig_pos] : ref->bdfc->orig_pos, | |
ref->xoff,ref->yoff,ref->selected?'S':'N' ); | |
} | |
fprintf( sfd, "EndBitmapFont\n" ); | |
return( err ); | |
} | |
static void SFDDumpPrivate(FILE *sfd,struct psdict *private) { | |
int i; | |
char *pt; | |
/* These guys should all be ascii text */ | |
fprintf( sfd, "BeginPrivate: %d\n", private->next ); | |
for ( i=0; i<private->next ; ++i ) { | |
fprintf( sfd, "%s %d ", private->keys[i], | |
(int)strlen(private->values[i])); | |
for ( pt = private->values[i]; *pt; ++pt ) | |
putc(*pt,sfd); | |
putc('\n',sfd); | |
} | |
fprintf( sfd, "EndPrivate\n" ); | |
} | |
static void SFDDumpLangName(FILE *sfd, struct ttflangname *ln) { | |
int i, end; | |
fprintf( sfd, "LangName: %d", ln->lang ); | |
for ( end = ttf_namemax; end>0 && ln->names[end-1]==NULL; --end ); | |
for ( i=0; i<end; ++i ) { | |
putc(' ',sfd); | |
SFDDumpUTF7Str(sfd,ln->names[i]); | |
} | |
putc('\n',sfd); | |
} | |
static void SFDDumpGasp(FILE *sfd, SplineFont *sf) { | |
int i; | |
if ( sf->gasp_cnt==0 ) | |
return; | |
fprintf( sfd, "GaspTable: %d", sf->gasp_cnt ); | |
for ( i=0; i<sf->gasp_cnt; ++i ) | |
fprintf( sfd, " %d %d", sf->gasp[i].ppem, sf->gasp[i].flags ); | |
fprintf( sfd, " %d", sf->gasp_version); | |
putc('\n',sfd); | |
} | |
static void SFDDumpDesignSize(FILE *sfd, SplineFont *sf) { | |
struct otfname *on; | |
if ( sf->design_size==0 ) | |
return; | |
fprintf( sfd, "DesignSize: %d", sf->design_size ); | |
if ( sf->fontstyle_id!=0 || sf->fontstyle_name!=NULL || | |
sf->design_range_bottom!=0 || sf->design_range_top!=0 ) { | |
fprintf( sfd, " %d-%d %d ", | |
sf->design_range_bottom, sf->design_range_top, | |
sf->fontstyle_id ); | |
for ( on=sf->fontstyle_name; on!=NULL; on=on->next ) { | |
fprintf( sfd, "%d ", on->lang ); | |
SFDDumpUTF7Str(sfd, on->name); | |
if ( on->next!=NULL ) putc(' ',sfd); | |
} | |
} | |
putc('\n',sfd); | |
} | |
static void SFDDumpOtfFeatNames(FILE *sfd, SplineFont *sf) { | |
struct otffeatname *fn; | |
struct otfname *on; | |
for ( fn=sf->feat_names; fn!=NULL; fn=fn->next ) { | |
fprintf( sfd, "OtfFeatName: '%c%c%c%c' ", | |
fn->tag>>24, fn->tag>>16, fn->tag>>8, fn->tag ); | |
for ( on=fn->names; on!=NULL; on=on->next ) { | |
fprintf( sfd, "%d ", on->lang ); | |
SFDDumpUTF7Str(sfd, on->name); | |
if ( on->next!=NULL ) putc(' ',sfd); | |
} | |
putc('\n',sfd); | |
} | |
} | |
static void putstring(FILE *sfd, const char *header, char *body) { | |
fprintf( sfd, "%s", header ); | |
while ( *body ) { | |
if ( *body=='\n' || *body == '\\' || *body=='\r' ) { | |
putc('\\',sfd); | |
if ( *body=='\\' ) | |
putc('\\',sfd); | |
else { | |
putc('n',sfd); | |
if ( *body=='\r' && body[1]=='\n' ) | |
++body; | |
} | |
} else | |
putc(*body,sfd); | |
++body; | |
} | |
putc('\n',sfd); | |
} | |
const char *EncName(Encoding *encname) { | |
return( encname->enc_name ); | |
} | |
static void SFDDumpEncoding(FILE *sfd,Encoding *encname,const char *keyword) { | |
fprintf(sfd, "%s: %s\n", keyword, encname->enc_name ); | |
} | |
static void SFDDumpMacName(FILE *sfd,struct macname *mn) { | |
char *pt; | |
while ( mn!=NULL ) { | |
fprintf( sfd, "MacName: %d %d %d \"", mn->enc, mn->lang, | |
(int)strlen(mn->name) ); | |
for ( pt=mn->name; *pt; ++pt ) { | |
if ( *pt<' ' || *pt>=0x7f || *pt=='\\' || *pt=='"' ) | |
fprintf( sfd, "\\%03o", *(uint8_t *) pt ); | |
else | |
putc(*pt,sfd); | |
} | |
fprintf( sfd, "\"\n" ); | |
mn = mn->next; | |
} | |
} | |
void SFDDumpMacFeat(FILE *sfd,MacFeat *mf) { | |
struct macsetting *ms; | |
if ( mf==NULL ) | |
return; | |
while ( mf!=NULL ) { | |
if ( mf->featname!=NULL ) { | |
fprintf( sfd, "MacFeat: %d %d %d\n", mf->feature, mf->ismutex, mf->default_setting ); | |
SFDDumpMacName(sfd,mf->featname); | |
for ( ms=mf->settings; ms!=NULL; ms=ms->next ) { | |
if ( ms->setname!=NULL ) { | |
fprintf( sfd, "MacSetting: %d\n", ms->setting ); | |
SFDDumpMacName(sfd,ms->setname); | |
} | |
} | |
} | |
mf = mf->next; | |
} | |
fprintf( sfd,"EndMacFeatures\n" ); | |
} | |
static void SFDDumpBaseLang(FILE *sfd,struct baselangextent *bl) { | |
if ( bl->lang==0 ) | |
fprintf( sfd, " { %d %d", bl->descent, bl->ascent ); | |
else | |
fprintf( sfd, " { '%c%c%c%c' %d %d", | |
bl->lang>>24, bl->lang>>16, bl->lang>>8, bl->lang, | |
bl->descent, bl->ascent ); | |
for ( bl=bl->features; bl!=NULL; bl=bl->next ) | |
SFDDumpBaseLang(sfd,bl); | |
putc('}',sfd); | |
} | |
static void SFDDumpBase(FILE *sfd,const char *keyword,struct Base *base) { | |
int i; | |
struct basescript *bs; | |
struct baselangextent *bl; | |
fprintf( sfd, "%s %d", keyword, base->baseline_cnt ); | |
for ( i=0; i<base->baseline_cnt; ++i ) { | |
fprintf( sfd, " '%c%c%c%c'", | |
base->baseline_tags[i]>>24, | |
base->baseline_tags[i]>>16, | |
base->baseline_tags[i]>>8, | |
base->baseline_tags[i]); | |
} | |
putc('\n',sfd); | |
for ( bs=base->scripts; bs!=NULL; bs=bs->next ) { | |
fprintf( sfd, "BaseScript: '%c%c%c%c' %d ", | |
bs->script>>24, bs->script>>16, bs->script>>8, bs->script, | |
bs->def_baseline ); | |
for ( i=0; i<base->baseline_cnt; ++i ) | |
fprintf( sfd, " %d", bs->baseline_pos[i]); | |
for ( bl=bs->langs; bl!=NULL; bl=bl->next ) | |
SFDDumpBaseLang(sfd,bl); | |
putc('\n',sfd); | |
} | |
} | |
static void SFDDumpJSTFLookups(FILE *sfd,const char *keyword, OTLookup **list ) { | |
int i; | |
if ( list==NULL || list[0]==NULL ) | |
return; | |
fprintf( sfd, "%s ", keyword ); | |
for ( i=0; list[i]!=NULL; ++i ) { | |
SFDDumpUTF7Str(sfd,list[i]->lookup_name); | |
if ( list[i+1]!=NULL ) putc(' ',sfd); | |
} | |
putc('\n',sfd); | |
} | |
static void SFDDumpJustify(FILE *sfd,SplineFont *sf) { | |
Justify *jscript; | |
struct jstf_lang *jlang; | |
int i; | |
for ( jscript = sf->justify; jscript!=NULL; jscript=jscript->next ) { | |
fprintf( sfd, "Justify: '%c%c%c%c'\n", | |
jscript->script>>24, | |
jscript->script>>16, | |
jscript->script>>8, | |
jscript->script); | |
if ( jscript->extenders!=NULL ) | |
fprintf( sfd, "JstfExtender: %s\n", jscript->extenders ); | |
for ( jlang = jscript->langs; jlang!=NULL; jlang = jlang->next ) { | |
fprintf( sfd, "JstfLang: '%c%c%c%c' %d\n", | |
jlang->lang>>24, | |
jlang->lang>>16, | |
jlang->lang>>8, | |
jlang->lang, jlang->cnt ); | |
for ( i=0; i<jlang->cnt; ++i ) { | |
fprintf( sfd, "JstfPrio:\n" ); | |
SFDDumpJSTFLookups(sfd,"JstfEnableShrink:", jlang->prios[i].enableShrink ); | |
SFDDumpJSTFLookups(sfd,"JstfDisableShrink:", jlang->prios[i].disableShrink ); | |
SFDDumpJSTFLookups(sfd,"JstfMaxShrink:", jlang->prios[i].maxShrink ); | |
SFDDumpJSTFLookups(sfd,"JstfEnableExtend:", jlang->prios[i].enableExtend ); | |
SFDDumpJSTFLookups(sfd,"JstfDisableExtend:", jlang->prios[i].disableExtend ); | |
SFDDumpJSTFLookups(sfd,"JstfMaxExtend:", jlang->prios[i].maxExtend ); | |
} | |
} | |
} | |
if ( sf->justify!=NULL ) | |
fprintf( sfd, "EndJustify\n" ); | |
} | |
static void SFDFpstClassNamesOut(FILE *sfd,int class_cnt,char **classnames,const char *keyword) { | |
char buffer[20]; | |
int i; | |
if ( class_cnt>0 && classnames!=NULL ) { | |
fprintf( sfd, " %s: ", keyword ); | |
for ( i=0; i<class_cnt; ++i ) { | |
if ( classnames[i]==NULL ) { | |
sprintf( buffer,"%d", i ); | |
SFDDumpUTF7Str(sfd,buffer); | |
} else | |
SFDDumpUTF7Str(sfd,classnames[i]); | |
if ( i<class_cnt-1 ) putc(' ',sfd); | |
} | |
putc('\n',sfd); | |
} | |
} | |
FILE* MakeTemporaryFile(void) { | |
FILE *ret = NULL; | |
#ifndef __MINGW32__ | |
gchar *loc; | |
int fd = g_file_open_tmp("fontforge-XXXXXX", &loc, NULL); | |
if (fd != -1) { | |
ret = fdopen(fd, "w+"); | |
g_unlink(loc); | |
g_free(loc); | |
} | |
#else | |
HANDLE hFile = INVALID_HANDLE_VALUE; | |
for (int retries = 0; hFile == INVALID_HANDLE_VALUE && retries < 10; retries++) { | |
wchar_t *temp = _wtempnam(NULL, L"FontForge"); | |
hFile = CreateFileW(temp, GENERIC_READ|GENERIC_WRITE, 0, NULL, | |
CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); | |
free(temp); | |
} | |
ret = _fdopen(_open_osfhandle((intptr_t)hFile, 0), "wb+"); | |
if (ret == NULL && hFile != INVALID_HANDLE_VALUE) { | |
CloseHandle(hFile); | |
} | |
#endif | |
return ret; | |
} | |
/** | |
* Read an entire file from the given open file handle and return that data | |
* as an allocated string that the caller must free. | |
* If any read or memory error occurs, then free string and return 0. | |
* FIXME: Use a better method than fseek() and ftell() since this does not | |
* play well with stdin, streaming input type files, or files with NULLs | |
*/ | |
char* FileToAllocatedString( FILE *f ) { | |
char *ret, *buf; | |
long fsize = 0; | |
size_t bread = 0; | |
/* get approximate file size, and allocate some memory */ | |
if ( fseek(f,0,SEEK_END)==0 && \ | |
(fsize=ftell(f))!=-1 && \ | |
fseek(f,0,SEEK_SET)==0 && \ | |
(buf=calloc(fsize+30001,1))!=NULL ) { | |
/* fread in file, size=non-exact, then resize memory smaller */ | |
bread=fread(buf,1,fsize+30000,f); | |
if ( bread<=0 || bread >=(size_t)fsize+30000 || (ret=realloc(buf,bread+1))==NULL ) { | |
free( buf ); | |
} else { | |
ret[bread] = '\0'; | |
return( ret ); | |
} | |
} | |
/* error occurred reading in file */ | |
fprintf(stderr,_("Failed to read a file. Bytes read:%ld file size:%ld\n"),(long)(bread),fsize ); | |
return( 0 ); | |
} | |
void SFD_DumpLookup( FILE *sfd, SplineFont *sf ) { | |
int isgpos; | |
OTLookup *otl; | |
struct lookup_subtable *sub; | |
FeatureScriptLangList *fl; | |
struct scriptlanglist *sl; | |
int i; | |
for ( isgpos=0; isgpos<2; ++isgpos ) { | |
for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) { | |
fprintf( sfd, "Lookup: %d %d %d ", otl->lookup_type, otl->lookup_flags, otl->store_in_afm ); | |
SFDDumpUTF7Str(sfd,otl->lookup_name); | |
fprintf( sfd, " { " ); | |
for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { | |
SFDDumpUTF7Str(sfd,sub->subtable_name); | |
putc(' ',sfd); | |
if ( otl->lookup_type==gsub_single && sub->suffix!=NULL ) { | |
putc('(',sfd); | |
SFDDumpUTF7Str(sfd,sub->suffix); | |
putc(')',sfd); | |
} else if ( otl->lookup_type==gpos_pair && sub->vertical_kerning ) | |
fprintf(sfd,"(1)"); | |
if ( otl->lookup_type==gpos_pair && (sub->separation!=0 || sub->kerning_by_touch)) | |
fprintf(sfd,"[%d,%d,%d]", sub->separation, sub->minkern, sub->kerning_by_touch+2*sub->onlyCloser+4*sub->dontautokern ); | |
putc(' ',sfd); | |
} | |
fprintf( sfd, "} [" ); | |
for ( fl=otl->features; fl!=NULL; fl=fl->next ) { | |
if ( fl->ismac ) | |
fprintf( sfd, "<%d,%d> (", | |
(int) (fl->featuretag>>16), | |
(int) (fl->featuretag&0xffff)); | |
else | |
fprintf( sfd, "'%c%c%c%c' (", | |
(int) (fl->featuretag>>24), (int) ((fl->featuretag>>16)&0xff), | |
(int) ((fl->featuretag>>8)&0xff), (int) (fl->featuretag&0xff) ); | |
for ( sl= fl->scripts; sl!=NULL; sl = sl->next ) { | |
fprintf( sfd, "'%c%c%c%c' <", | |
(int) (sl->script>>24), (int) ((sl->script>>16)&0xff), | |
(int) ((sl->script>>8)&0xff), (int) (sl->script&0xff) ); | |
for ( i=0; i<sl->lang_cnt; ++i ) { | |
uint32_t lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG]; | |
fprintf( sfd, "'%c%c%c%c' ", | |
(int) (lang>>24), (int) ((lang>>16)&0xff), | |
(int) ((lang>>8)&0xff), (int) (lang&0xff) ); | |
} | |
fprintf( sfd, "> " ); | |
} | |
fprintf( sfd, ") " ); | |
} | |
fprintf( sfd, "]\n" ); | |
} | |
} | |
} | |
int SFD_DumpSplineFontMetadata( FILE *sfd, SplineFont *sf ) | |
{ | |
int i, j; | |
struct ttflangname *ln; | |
struct ttf_table *tab; | |
KernClass *kc; | |
FPST *fpst; | |
ASM *sm; | |
int isv; | |
int err = false; | |
int isgpos; | |
OTLookup *otl; | |
struct lookup_subtable *sub; | |
FeatureScriptLangList *fl; | |
struct scriptlanglist *sl; | |
fprintf(sfd, "FontName: %s\n", sf->fontname ); | |
if ( sf->fullname!=NULL ) | |
fprintf(sfd, "FullName: %s\n", sf->fullname ); | |
if ( sf->familyname!=NULL ) | |
fprintf(sfd, "FamilyName: %s\n", sf->familyname ); | |
if ( sf->weight!=NULL ) | |
fprintf(sfd, "Weight: %s\n", sf->weight ); | |
if ( sf->copyright!=NULL ) | |
putstring(sfd, "Copyright: ", sf->copyright ); | |
if ( sf->comments!=NULL ) { | |
fprintf( sfd, "UComments: " ); | |
SFDDumpUTF7Str(sfd,sf->comments); | |
putc('\n',sfd); | |
} | |
if ( sf->fontlog!=NULL ) { | |
fprintf( sfd, "FontLog: " ); | |
SFDDumpUTF7Str(sfd,sf->fontlog); | |
putc('\n',sfd); | |
} | |
if ( sf->version!=NULL ) | |
fprintf(sfd, "Version: %s\n", sf->version ); | |
if ( sf->styleMapFamilyName!=NULL ) | |
fprintf(sfd, "StyleMapFamilyName: %s\n", sf->styleMapFamilyName ); | |
if ( sf->fondname!=NULL ) | |
fprintf(sfd, "FONDName: %s\n", sf->fondname ); | |
if ( sf->defbasefilename!=NULL ) | |
fprintf(sfd, "DefaultBaseFilename: %s\n", sf->defbasefilename ); | |
if ( sf->strokewidth!=0 ) | |
fprintf(sfd, "StrokeWidth: %g\n", (double) sf->strokewidth ); | |
fprintf(sfd, "ItalicAngle: %g\n", (double) sf->italicangle ); | |
fprintf(sfd, "UnderlinePosition: %g\n", (double) sf->upos ); | |
fprintf(sfd, "UnderlineWidth: %g\n", (double) sf->uwidth ); | |
fprintf(sfd, "Ascent: %d\n", sf->ascent ); | |
fprintf(sfd, "Descent: %d\n", sf->descent ); | |
fprintf(sfd, "InvalidEm: %d\n", sf->invalidem ); | |
if ( sf->sfntRevision!=sfntRevisionUnset ) | |
fprintf(sfd, "sfntRevision: 0x%08x\n", sf->sfntRevision ); | |
if ( sf->woffMajor!=woffUnset ) { | |
fprintf(sfd, "woffMajor: %d\n", sf->woffMajor ); | |
fprintf(sfd, "woffMinor: %d\n", sf->woffMinor ); | |
} | |
if ( sf->woffMetadata!=NULL ) { | |
fprintf( sfd, "woffMetadata: " ); | |
SFDDumpUTF7Str(sfd,sf->woffMetadata); | |
putc('\n',sfd); | |
} | |
if ( sf->ufo_ascent!=0 ) | |
fprintf(sfd, "UFOAscent: %g\n", (double) sf->ufo_ascent ); | |
if ( sf->ufo_descent!=0 ) | |
fprintf(sfd, "UFODescent: %g\n", (double) sf->ufo_descent ); | |
fprintf(sfd, "LayerCount: %d\n", sf->layer_cnt ); | |
for ( i=0; i<sf->layer_cnt; ++i ) { | |
fprintf( sfd, "Layer: %d %d ", i, sf->layers[i].order2/*, sf->layers[i].background*/ ); | |
SFDDumpUTF7Str(sfd,sf->layers[i].name); | |
fprintf( sfd, " %d", sf->layers[i].background ); | |
if (sf->layers[i].ufo_path != NULL) { putc(' ',sfd); SFDDumpUTF7Str(sfd,sf->layers[i].ufo_path); } | |
putc('\n',sfd); | |
} | |
// TODO: Output U. F. O. layer path. | |
if (sf->preferred_kerning != 0) fprintf(sfd, "PreferredKerning: %d\n", sf->preferred_kerning); | |
if ( sf->strokedfont ) | |
fprintf(sfd, "StrokedFont: %d\n", sf->strokedfont ); | |
else if ( sf->multilayer ) | |
fprintf(sfd, "MultiLayer: %d\n", sf->multilayer ); | |
if ( sf->hasvmetrics ) | |
fprintf(sfd, "HasVMetrics: %d\n", sf->hasvmetrics ); | |
if ( sf->use_xuid && sf->changed_since_xuidchanged ) | |
fprintf(sfd, "NeedsXUIDChange: 1\n" ); | |
if ( sf->xuid!=NULL ) | |
fprintf(sfd, "XUID: %s\n", sf->xuid ); | |
if ( sf->uniqueid!=0 ) | |
fprintf(sfd, "UniqueID: %d\n", sf->uniqueid ); | |
if ( sf->use_xuid ) | |
fprintf(sfd, "UseXUID: 1\n" ); | |
if ( sf->use_uniqueid ) | |
fprintf(sfd, "UseUniqueID: 1\n" ); | |
if ( sf->horiz_base!=NULL ) | |
SFDDumpBase(sfd,"BaseHoriz:",sf->horiz_base); | |
if ( sf->vert_base!=NULL ) | |
SFDDumpBase(sfd,"BaseVert:",sf->vert_base); | |
if ( sf->pfminfo.stylemap!=-1 ) | |
fprintf(sfd, "StyleMap: 0x%04x\n", sf->pfminfo.stylemap ); | |
if ( sf->pfminfo.fstype!=-1 ) | |
fprintf(sfd, "FSType: %d\n", sf->pfminfo.fstype ); | |
fprintf(sfd, "OS2Version: %d\n", sf->os2_version ); | |
fprintf(sfd, "OS2_WeightWidthSlopeOnly: %d\n", sf->weight_width_slope_only ); | |
fprintf(sfd, "OS2_UseTypoMetrics: %d\n", sf->use_typo_metrics ); | |
fprintf(sfd, "CreationTime: %lld\n", sf->creationtime ); | |
fprintf(sfd, "ModificationTime: %lld\n", sf->modificationtime ); | |
if ( sf->pfminfo.pfmset ) { | |
fprintf(sfd, "PfmFamily: %d\n", sf->pfminfo.pfmfamily ); | |
fprintf(sfd, "TTFWeight: %d\n", sf->pfminfo.weight ); | |
fprintf(sfd, "TTFWidth: %d\n", sf->pfminfo.width ); | |
fprintf(sfd, "LineGap: %d\n", sf->pfminfo.linegap ); | |
fprintf(sfd, "VLineGap: %d\n", sf->pfminfo.vlinegap ); | |
/*putc('\n',sfd);*/ | |
} | |
if ( sf->pfminfo.panose_set ) { | |
fprintf(sfd, "Panose:" ); | |
for ( i=0; i<10; ++i ) | |
fprintf( sfd, " %d", sf->pfminfo.panose[i]); | |
putc('\n',sfd); | |
} | |
fprintf(sfd, "OS2TypoAscent: %d\n", sf->pfminfo.os2_typoascent ); | |
fprintf(sfd, "OS2TypoAOffset: %d\n", sf->pfminfo.typoascent_add ); | |
fprintf(sfd, "OS2TypoDescent: %d\n", sf->pfminfo.os2_typodescent ); | |
fprintf(sfd, "OS2TypoDOffset: %d\n", sf->pfminfo.typodescent_add ); | |
fprintf(sfd, "OS2TypoLinegap: %d\n", sf->pfminfo.os2_typolinegap ); | |
fprintf(sfd, "OS2WinAscent: %d\n", sf->pfminfo.os2_winascent ); | |
fprintf(sfd, "OS2WinAOffset: %d\n", sf->pfminfo.winascent_add ); | |
fprintf(sfd, "OS2WinDescent: %d\n", sf->pfminfo.os2_windescent ); | |
fprintf(sfd, "OS2WinDOffset: %d\n", sf->pfminfo.windescent_add ); | |
fprintf(sfd, "HheadAscent: %d\n", sf->pfminfo.hhead_ascent ); | |
fprintf(sfd, "HheadAOffset: %d\n", sf->pfminfo.hheadascent_add ); | |
fprintf(sfd, "HheadDescent: %d\n", sf->pfminfo.hhead_descent ); | |
fprintf(sfd, "HheadDOffset: %d\n", sf->pfminfo.hheaddescent_add ); | |
if ( sf->pfminfo.subsuper_set ) { | |
fprintf(sfd, "OS2SubXSize: %d\n", sf->pfminfo.os2_subxsize ); | |
fprintf(sfd, "OS2SubYSize: %d\n", sf->pfminfo.os2_subysize ); | |
fprintf(sfd, "OS2SubXOff: %d\n", sf->pfminfo.os2_subxoff ); | |
fprintf(sfd, "OS2SubYOff: %d\n", sf->pfminfo.os2_subyoff ); | |
fprintf(sfd, "OS2SupXSize: %d\n", sf->pfminfo.os2_supxsize ); | |
fprintf(sfd, "OS2SupYSize: %d\n", sf->pfminfo.os2_supysize ); | |
fprintf(sfd, "OS2SupXOff: %d\n", sf->pfminfo.os2_supxoff ); | |
fprintf(sfd, "OS2SupYOff: %d\n", sf->pfminfo.os2_supyoff ); | |
fprintf(sfd, "OS2StrikeYSize: %d\n", sf->pfminfo.os2_strikeysize ); | |
fprintf(sfd, "OS2StrikeYPos: %d\n", sf->pfminfo.os2_strikeypos ); | |
} | |
if ( sf->pfminfo.os2_capheight!=0 ) | |
fprintf(sfd, "OS2CapHeight: %d\n", sf->pfminfo.os2_capheight ); | |
if ( sf->pfminfo.os2_xheight!=0 ) | |
fprintf(sfd, "OS2XHeight: %d\n", sf->pfminfo.os2_xheight ); | |
if ( sf->pfminfo.os2_family_class!=0 ) | |
fprintf(sfd, "OS2FamilyClass: %d\n", sf->pfminfo.os2_family_class ); | |
if ( sf->pfminfo.os2_vendor[0]!='\0' ) { | |
fprintf(sfd, "OS2Vendor: '%c%c%c%c'\n", | |
sf->pfminfo.os2_vendor[0], sf->pfminfo.os2_vendor[1], | |
sf->pfminfo.os2_vendor[2], sf->pfminfo.os2_vendor[3] ); | |
} | |
if ( sf->pfminfo.hascodepages ) | |
fprintf(sfd, "OS2CodePages: %08x.%08x\n", sf->pfminfo.codepages[0], sf->pfminfo.codepages[1]); | |
if ( sf->pfminfo.hasunicoderanges ) | |
fprintf(sfd, "OS2UnicodeRanges: %08x.%08x.%08x.%08x\n", | |
sf->pfminfo.unicoderanges[0], sf->pfminfo.unicoderanges[1], | |
sf->pfminfo.unicoderanges[2], sf->pfminfo.unicoderanges[3] ); | |
if ( sf->macstyle!=-1 ) | |
fprintf(sfd, "MacStyle: %d\n", sf->macstyle ); | |
/* Must come before any kerning classes, anchor classes, conditional psts */ | |
/* state machines, psts, kerning pairs, etc. */ | |
for ( isgpos=0; isgpos<2; ++isgpos ) { | |
for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl = otl->next ) { | |
fprintf( sfd, "Lookup: %d %d %d ", otl->lookup_type, otl->lookup_flags, otl->store_in_afm ); | |
SFDDumpUTF7Str(sfd,otl->lookup_name); | |
fprintf( sfd, " { " ); | |
for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) { | |
SFDDumpUTF7Str(sfd,sub->subtable_name); | |
putc(' ',sfd); | |
if ( otl->lookup_type==gsub_single && sub->suffix!=NULL ) { | |
putc('(',sfd); | |
SFDDumpUTF7Str(sfd,sub->suffix); | |
putc(')',sfd); | |
} else if ( otl->lookup_type==gpos_pair && sub->vertical_kerning ) | |
fprintf(sfd,"(1)"); | |
if ( otl->lookup_type==gpos_pair && (sub->separation!=0 || sub->kerning_by_touch)) | |
fprintf(sfd,"[%d,%d,%d]", sub->separation, sub->minkern, sub->kerning_by_touch+2*sub->onlyCloser+4*sub->dontautokern ); | |
putc(' ',sfd); | |
} | |
fprintf( sfd, "} [" ); | |
for ( fl=otl->features; fl!=NULL; fl=fl->next ) { | |
if ( fl->ismac ) | |
fprintf( sfd, "<%d,%d> (", | |
(int) (fl->featuretag>>16), | |
(int) (fl->featuretag&0xffff)); | |
else | |
fprintf( sfd, "'%c%c%c%c' (", | |
(int) (fl->featuretag>>24), (int) ((fl->featuretag>>16)&0xff), | |
(int) ((fl->featuretag>>8)&0xff), (int) (fl->featuretag&0xff) ); | |
for ( sl= fl->scripts; sl!=NULL; sl = sl->next ) { | |
fprintf( sfd, "'%c%c%c%c' <", | |
(int) (sl->script>>24), (int) ((sl->script>>16)&0xff), | |
(int) ((sl->script>>8)&0xff), (int) (sl->script&0xff) ); | |
for ( i=0; i<sl->lang_cnt; ++i ) { | |
uint32_t lang = i<MAX_LANG ? sl->langs[i] : sl->morelangs[i-MAX_LANG]; | |
fprintf( sfd, "'%c%c%c%c' ", | |
(int) (lang>>24), (int) ((lang>>16)&0xff), | |
(int) ((lang>>8)&0xff), (int) (lang&0xff) ); | |
} | |
fprintf( sfd, "> " ); | |
} | |
fprintf( sfd, ") " ); | |
} | |
fprintf( sfd, "]\n" ); | |
} | |
} | |
if ( sf->mark_class_cnt!=0 ) { | |
fprintf( sfd, "MarkAttachClasses: %d\n", sf->mark_class_cnt ); | |
for ( i=1; i<sf->mark_class_cnt; ++i ) { /* Class 0 is unused */ | |
SFDDumpUTF7Str(sfd, sf->mark_class_names[i]); | |
putc(' ',sfd); | |
if ( sf->mark_classes[i]!=NULL ) | |
fprintf( sfd, "%d %s\n", (int) strlen(sf->mark_classes[i]), | |
sf->mark_classes[i] ); | |
else | |
fprintf( sfd, "0 \n" ); | |
} | |
} | |
if ( sf->mark_set_cnt!=0 ) { | |
fprintf( sfd, "MarkAttachSets: %d\n", sf->mark_set_cnt ); | |
for ( i=0; i<sf->mark_set_cnt; ++i ) { /* Set 0 is used */ | |
SFDDumpUTF7Str(sfd, sf->mark_set_names[i]); | |
putc(' ',sfd); | |
if ( sf->mark_sets[i]!=NULL ) | |
fprintf( sfd, "%d %s\n", (int) strlen(sf->mark_sets[i]), | |
sf->mark_sets[i] ); | |
else | |
fprintf( sfd, "0 \n" ); | |
} | |
} | |
fprintf( sfd, "DEI: 91125\n" ); | |
for ( isv=0; isv<2; ++isv ) { | |
for ( kc=isv ? sf->vkerns : sf->kerns; kc!=NULL; kc = kc->next ) { | |
if (kc->firsts_names == NULL && kc->seconds_names == NULL && kc->firsts_flags == NULL && kc->seconds_flags == NULL) { | |
fprintf( sfd, "%s: %d%s %d ", isv ? "VKernClass2" : "KernClass2", | |
kc->first_cnt, kc->firsts[0]!=NULL?"+":"", | |
kc->second_cnt ); | |
SFDDumpUTF7Str(sfd,kc->subtable->subtable_name); | |
putc('\n',sfd); | |
if ( kc->firsts[0]!=NULL ) | |
fprintf( sfd, " %d %s\n", (int)strlen(kc->firsts[0]), | |
kc->firsts[0]); | |
for ( i=1; i<kc->first_cnt; ++i ) | |
fprintf( sfd, " %d %s\n", (int)strlen(kc->firsts[i]), | |
kc->firsts[i]); | |
for ( i=1; i<kc->second_cnt; ++i ) | |
fprintf( sfd, " %d %s\n", (int)strlen(kc->seconds[i]), | |
kc->seconds[i]); | |
for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) { | |
fprintf( sfd, " %d", kc->offsets[i]); | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,&kc->adjusts[i]); | |
} | |
fprintf( sfd, "\n" ); | |
} else { | |
fprintf( sfd, "%s: %d%s %d ", isv ? "VKernClass3" : "KernClass3", | |
kc->first_cnt, kc->firsts[0]!=NULL?"+":"", | |
kc->second_cnt ); | |
SFDDumpUTF7Str(sfd,kc->subtable->subtable_name); | |
putc('\n',sfd); | |
if ( kc->firsts[0]!=NULL ) { | |
fprintf( sfd, " %d ", ((kc->firsts_flags && kc->firsts_flags[0]) ? kc->firsts_flags[0] : 0)); | |
SFDDumpUTF7Str(sfd, ((kc->firsts_names && kc->firsts_names[0]) ? kc->firsts_names[0] : "")); | |
fprintf( sfd, " " ); | |
SFDDumpUTF7Str(sfd,kc->firsts[0]); | |
fprintf( sfd, "\n" ); | |
} | |
for ( i=1; i<kc->first_cnt; ++i ) { | |
fprintf( sfd, " %d ", ((kc->firsts_flags && kc->firsts_flags[i]) ? kc->firsts_flags[i] : 0)); | |
SFDDumpUTF7Str(sfd, ((kc->firsts_names && kc->firsts_names[i]) ? kc->firsts_names[i] : "")); | |
fprintf( sfd, " " ); | |
SFDDumpUTF7Str(sfd,kc->firsts[i]); | |
fprintf( sfd, "\n" ); | |
} | |
for ( i=1; i<kc->second_cnt; ++i ) { | |
fprintf( sfd, " %d ", ((kc->seconds_flags && kc->seconds_flags[i]) ? kc->seconds_flags[i] : 0)); | |
SFDDumpUTF7Str(sfd, ((kc->seconds_names && kc->seconds_names[i]) ? kc->seconds_names[i] : "")); | |
fprintf( sfd, " " ); | |
SFDDumpUTF7Str(sfd,kc->seconds[i]); | |
fprintf( sfd, "\n" ); | |
} | |
for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) { | |
fprintf( sfd, " %d %d", ((kc->offsets_flags && kc->offsets_flags[i]) ? kc->offsets_flags[i] : 0), kc->offsets[i]); | |
putc(' ',sfd); | |
SFDDumpDeviceTable(sfd,&kc->adjusts[i]); | |
} | |
fprintf( sfd, "\n" ); | |
} | |
} | |
} | |
for ( fpst=sf->possub; fpst!=NULL; fpst=fpst->next ) { | |
static const char *keywords[] = { "ContextPos2:", "ContextSub2:", "ChainPos2:", "ChainSub2:", "ReverseChain2:", NULL }; | |
static const char *formatkeys[] = { "glyph", "class", "coverage", "revcov", NULL }; | |
fprintf( sfd, "%s %s ", keywords[fpst->type-pst_contextpos], | |
formatkeys[fpst->format] ); | |
SFDDumpUTF7Str(sfd,fpst->subtable->subtable_name); | |
fprintf( sfd, " %d %d %d %d\n", | |
fpst->nccnt, fpst->bccnt, fpst->fccnt, fpst->rule_cnt ); | |
if ( fpst->nccnt>0 && fpst->nclass[0]!=NULL ) | |
fprintf( sfd, " Class0: %d %s\n", (int)strlen(fpst->nclass[0]), | |
fpst->nclass[0]); | |
for ( i=1; i<fpst->nccnt; ++i ) | |
fprintf( sfd, " Class: %d %s\n", (int)strlen(fpst->nclass[i]), | |
fpst->nclass[i]); | |
for ( i=1; i<fpst->bccnt; ++i ) | |
fprintf( sfd, " BClass: %d %s\n", (int)strlen(fpst->bclass[i]), | |
fpst->bclass[i]); | |
for ( i=1; i<fpst->fccnt; ++i ) | |
fprintf( sfd, " FClass: %d %s\n", (int)strlen(fpst->fclass[i]), | |
fpst->fclass[i]); | |
for ( i=0; i<fpst->rule_cnt; ++i ) { | |
switch ( fpst->format ) { | |
case pst_glyphs: | |
fprintf( sfd, " String: %d %s\n", | |
(int)strlen(fpst->rules[i].u.glyph.names), | |
fpst->rules[i].u.glyph.names); | |
if ( fpst->rules[i].u.glyph.back!=NULL ) | |
fprintf( sfd, " BString: %d %s\n", | |
(int)strlen(fpst->rules[i].u.glyph.back), | |
fpst->rules[i].u.glyph.back); | |
else | |
fprintf( sfd, " BString: 0\n"); | |