Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

9349 lines (8806 sloc) 287.743 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 "baseviews.h"
#include "views.h"
#include <gdraw.h>
#include <ustring.h>
#include <math.h>
#include <utype.h>
#include <unistd.h>
#include <locale.h>
#include <gfile.h>
#include <gwidget.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h> /* For NAME_MAX or _POSIX_NAME_MAX */
#define GTimer GTimer_GTK
#include <glib.h>
#undef GTimer
#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;
#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",
"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 SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids);
static RefChar *SFDGetRef(FILE *sfd, int was_enc);
static void SFDDumpImage(FILE *sfd,ImageList *img);
static AnchorPoint *SFDReadAnchorPoints(FILE *sfd,SplineChar *sc,AnchorPoint** alist, AnchorPoint *lastap);
static void SFDDumpTtfInstrs(FILE *sfd,SplineChar *sc);
static void SFDDumpTtfInstrsExplicit(FILE *sfd,uint8 *ttf_instrs, int16 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 );
extern void ExtractHints(SplineChar *sc,void *hints,int docopy);
extern void *UHintCopy(SplineChar *sc,int docopy);
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) {
SplinePoint *first, *sp;
int order2 = spl->first->next==NULL || spl->first->next->order2;
for ( ; spl!=NULL; spl=spl->next ) {
first = NULL;
for ( sp = spl->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 );
}
}
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;
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 *image2rle(struct _GImage *img, int *len) {
int max = img->height*img->bytes_per_line;
uint8 *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)), 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 ) {
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 );
}
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 *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 *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 *ipt = (uint32 *) (base->data + i*base->bytes_per_line);
uint32 *iend = (uint32 *) (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 *pt = (uint8 *) (base->data + i*base->bytes_per_line);
uint8 *end = (uint8 *) (base->data + (i+1)*base->bytes_per_line);
while ( pt<end ) {
SFDEnc85(&enc,*pt);
++pt;
}
}
}
}
SFDEnc85EndEnc(&enc);
fprintf(sfd,"\nEndImage\n" );
}
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 *ttf_instrs, int16 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 *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*) tab->data;
ended = tab->tag!=CHR('c','v','t',' ') || sf->cvt_names==NULL;
for ( i=0; i<(tab->len>>1); ++i ) {
int num = (int16) ((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 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 0
// This is now layer-specific.
if ( sc->python_persistent!=NULL )
SFDPickleMe(sfd,sc->python_persistent,sc->python_persistent_has_lists);
#endif // 0
#if HANYANG
if ( sc->compositionunit )
fprintf( sfd, "CompositionUnit: %d %d\n", sc->jamo, sc->varient );
#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 )
SFDDumpImage(sfd,img);
if ( sc->layers[i].splines!=NULL ) {
fprintf(sfd, "SplineSet\n" );
SFDDumpSplineSet(sfd,sc->layers[i].splines);
}
SFDDumpRefs(sfd,sc->layers[i].refs,newgids);
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->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 *pt = (uint8 *) (bfc->bitmap + i*bfc->bytes_per_line);
uint8 *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();
fprintf( sfd, "BitmapFont: %d %d %d %d %d %s\n", bdf->pixelsize, bdf->glyphcnt,
bdf->ascent, bdf->descent, BDFDepth(bdf), bdf->foundry?bdf->foundry:"" );
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 *) 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);
}
}
/**
* Get the path name of /tmp or equivalent on the current system.
* The return value should not be freed by the caller
*/
static const char* getSlashTempName(void) {
char* t = 0;
if((t=getenv("TMPDIR"))) {
return t;
}
#ifndef P_tmpdir
#define P_tmpdir "/tmp"
#endif
return P_tmpdir;
}
FILE* MakeTemporaryFile(void) {
FILE * ret;
char template[PATH_MAX+1];
int fd;
strncpy( template, getSlashTempName(), PATH_MAX-2-strlen("fontforge-stemp-XXXXXX") );
strcat( template, "/" );
strcat( template, "fontforge-stemp-XXXXXX" );
fd = g_mkstemp( template );
printf("MakeTemporaryFile() fd:%d template:%s\n", fd, template );
if ( (ret=fdopen(fd,"rw+"))==NULL ) ret=0;
unlink( template );
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 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 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");
if ( fpst->rules[i].u.glyph.fore!=NULL )
fprintf( sfd, " FString: %d %s\n",
(int)strlen(fpst->rules[i].u.glyph.fore),
fpst->rules[i].u.glyph.fore);
else
fprintf( sfd, " FString: 0\n");
break;
case pst_class:
fprintf( sfd, " %d %d %d\n ClsList:", fpst->rules[i].u.class.ncnt, fpst->rules[i].u.class.bcnt, fpst->rules[i].u.class.fcnt );
for ( j=0; j<fpst->rules[i].u.class.ncnt; ++j )
fprintf( sfd, " %d", fpst->rules[i].u.class.nclasses[j]);
fprintf( sfd, "\n BClsList:" );
for ( j=0; j<fpst->rules[i].u.class.bcnt; ++j )
fprintf( sfd, " %d", fpst->rules[i].u.class.bclasses[j]);
fprintf( sfd, "\n FClsList:" );
for ( j=0; j<fpst->rules[i].u.class.fcnt; ++j )
fprintf( sfd, " %d", fpst->rules[i].u.class.fclasses[j]);
fprintf( sfd, "\n" );
break;
case pst_coverage:
case pst_reversecoverage:
fprintf( sfd, " %d %d %d\n", fpst->rules[i].u.coverage.ncnt, fpst->rules[i].u.coverage.bcnt, fpst->rules[i].u.coverage.fcnt );
for ( j=0; j<fpst->rules[i].u.coverage.ncnt; ++j )
fprintf( sfd, " Coverage: %d %s\n",
(int)strlen(fpst->rules[i].u.coverage.ncovers[j]),
fpst->rules[i].u.coverage.ncovers[j]);
for ( j=0; j<fpst->rules[i].u.coverage.bcnt; ++j )
fprintf( sfd, " BCoverage: %d %s\n",
(int)strlen(fpst->rules[i].u.coverage.bcovers[j]),
fpst->rules[i].u.coverage.bcovers[j]);
for ( j=0; j<fpst->rules[i].u.coverage.fcnt; ++j )
fprintf( sfd, " FCoverage: %d %s\n",
(int)strlen(fpst->rules[i].u.coverage.fcovers[j]),
fpst->rules[i].u.coverage.fcovers[j]);
break;
default:
break;
}
switch ( fpst->format ) {
case pst_glyphs:
case pst_class:
case pst_coverage:
fprintf( sfd, " %d\n", fpst->rules[i].lookup_cnt );
for ( j=0; j<fpst->rules[i].lookup_cnt; ++j ) {
fprintf( sfd, " SeqLookup: %d ",
fpst->rules[i].lookups[j].seq );
SFDDumpUTF7Str(sfd,fpst->rules[i].lookups[j].lookup->lookup_name);
putc('\n',sfd);
}
break;
case pst_reversecoverage:
fprintf( sfd, " Replace: %d %s\n",
(int)strlen(fpst->rules[i].u.rcoverage.replacements),
fpst->rules[i].u.rcoverage.replacements);
break;
default:
break;
}
}
/* It would make more sense to output these up near the classes */
/* but that would break backwards compatibility (old parsers will */
/* ignore these entries if they are at the end, new parsers will */
/* read them */
SFDFpstClassNamesOut(sfd,fpst->nccnt,fpst->nclassnames,"ClassNames");
SFDFpstClassNamesOut(sfd,fpst->bccnt,fpst->bclassnames,"BClassNames");
SFDFpstClassNamesOut(sfd,fpst->fccnt,fpst->fclassnames,"FClassNames");
fprintf( sfd, "EndFPST\n" );
}
struct ff_glyphclasses *grouptmp;
for ( grouptmp = sf->groups; grouptmp != NULL; grouptmp = grouptmp->next ) {
fprintf(sfd, "Group: ");
SFDDumpUTF7Str(sfd, grouptmp->classname); fprintf(sfd, " ");
SFDDumpUTF7Str(sfd, grouptmp->glyphs); fprintf(sfd, "\n");
}
struct ff_rawoffsets *groupkerntmp;
for ( groupkerntmp = sf->groupkerns; groupkerntmp != NULL; groupkerntmp = groupkerntmp->next ) {
fprintf(sfd, "GroupKern: ");
SFDDumpUTF7Str(sfd, groupkerntmp->left); fprintf(sfd, " ");
SFDDumpUTF7Str(sfd, groupkerntmp->right); fprintf(sfd, " ");
fprintf(sfd, "%d\n", groupkerntmp->offset);
}
for ( groupkerntmp = sf->groupvkerns; groupkerntmp != NULL; groupkerntmp = groupkerntmp->next ) {
fprintf(sfd, "GroupVKern: ");
SFDDumpUTF7Str(sfd, groupkerntmp->left); fprintf(sfd, " ");
SFDDumpUTF7Str(sfd, groupkerntmp->right); fprintf(sfd, " ");
fprintf(sfd, "%d\n", groupkerntmp->offset);
}
for ( sm=sf->sm; sm!=NULL; sm=sm->next ) {
static const char *keywords[] = { "MacIndic2:", "MacContext2:", "MacLigature2:", "unused", "MacSimple2:", "MacInsert2:",
"unused", "unused", "unused", "unused", "unused", "unused",
"unused", "unused", "unused", "unused", "unused", "MacKern2:",
NULL };
fprintf( sfd, "%s ", keywords[sm->type-asm_indic] );
SFDDumpUTF7Str(sfd,sm->subtable->subtable_name);
fprintf( sfd, " %d %d %d\n", sm->flags, sm->class_cnt, sm->state_cnt );
for ( i=4; i<sm->class_cnt; ++i )
fprintf( sfd, " Class: %d %s\n", (int)strlen(sm->classes[i]),
sm->classes[i]);
for ( i=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
fprintf( sfd, " %d %d ", sm->state[i].next_state, sm->state[i].flags );
if ( sm->type==asm_context ) {
if ( sm->state[i].u.context.mark_lookup==NULL )
putc('~',sfd);
else
SFDDumpUTF7Str(sfd,sm->state[i].u.context.mark_lookup->lookup_name);
putc(' ',sfd);
if ( sm->state[i].u.context.cur_lookup==0 )
putc('~',sfd);
else
SFDDumpUTF7Str(sfd,sm->state[i].u.context.cur_lookup->lookup_name);
putc(' ',sfd);
} else if ( sm->type == asm_insert ) {
if ( sm->state[i].u.insert.mark_ins==NULL )
fprintf( sfd, "0 ");
else
fprintf( sfd, "%d %s ",
(int)strlen(sm->state[i].u.insert.mark_ins),
sm->state[i].u.insert.mark_ins );
if ( sm->state[i].u.insert.cur_ins==NULL )
fprintf( sfd, "0 ");
else
fprintf( sfd, "%d %s ",
(int)strlen(sm->state[i].u.insert.cur_ins),
sm->state[i].u.insert.cur_ins );
} else if ( sm->type == asm_kern ) {
fprintf( sfd, "%d ", sm->state[i].u.kern.kcnt );
for ( j=0; j<sm->state[i].u.kern.kcnt; ++j )
fprintf( sfd, "%d ", sm->state[i].u.kern.kerns[j]);
}
putc('\n',sfd);
}
fprintf( sfd, "EndASM\n" );
}
SFDDumpMacFeat(sfd,sf->features);
SFDDumpJustify(sfd,sf);
for ( tab = sf->ttf_tables; tab!=NULL ; tab = tab->next )
SFDDumpTtfTable(sfd,tab,sf);
for ( tab = sf->ttf_tab_saved; tab!=NULL ; tab = tab->next )
SFDDumpTtfTable(sfd,tab,sf);
for ( ln = sf->names; ln!=NULL; ln=ln->next )
SFDDumpLangName(sfd,ln);
if ( sf->gasp_cnt!=0 )
SFDDumpGasp(sfd,sf);
if ( sf->design_size!=0 )
SFDDumpDesignSize(sfd,sf);
if ( sf->feat_names!=NULL )
SFDDumpOtfFeatNames(sfd,sf);
return( err );
}
static int SFD_Dump( FILE *sfd, SplineFont *sf, EncMap *map, EncMap *normal,
int todir, char *dirname)
{
int i, realcnt;
BDFFont *bdf;
int *newgids = NULL;
int err = false;
if ( normal!=NULL )
map = normal;
SFD_DumpSplineFontMetadata( sfd, sf ); //, map, normal, todir, dirname );
if ( sf->MATH!=NULL ) {
struct MATH *math = sf->MATH;
for ( i=0; math_constants_descriptor[i].script_name!=NULL; ++i ) {
fprintf( sfd, "MATH:%s: %d", math_constants_descriptor[i].script_name,
*((int16 *) (((char *) (math)) + math_constants_descriptor[i].offset)) );
if ( math_constants_descriptor[i].devtab_offset>=0 ) {
DeviceTable **devtab = (DeviceTable **) (((char *) (math)) + math_constants_descriptor[i].devtab_offset );
putc(' ',sfd);
SFDDumpDeviceTable(sfd,*devtab);
}
putc('\n',sfd);
}
}
if ( sf->python_persistent!=NULL )
SFDPickleMe(sfd,sf->python_persistent, sf->python_persistent_has_lists);
if ( sf->subfontcnt!=0 ) {
/* CID fonts have no encodings, they have registry info instead */
fprintf(sfd, "Registry: %s\n", sf->cidregistry );
fprintf(sfd, "Ordering: %s\n", sf->ordering );
fprintf(sfd, "Supplement: %d\n", sf->supplement );
fprintf(sfd, "CIDVersion: %g\n", (double) sf->cidversion ); /* This is a number whereas "version" is a string */
} else
SFDDumpEncoding(sfd,map->enc,"Encoding");
if ( normal!=NULL )
fprintf(sfd, "Compacted: 1\n" );
fprintf( sfd, "UnicodeInterp: %s\n", unicode_interp_names[sf->uni_interp]);
fprintf( sfd, "NameList: %s\n", sf->for_new_glyphs->title );
if ( map->remap!=NULL ) {
struct remap *remap;
int n;
for ( n=0,remap = map->remap; remap->infont!=-1; ++n, ++remap );
fprintf( sfd, "RemapN: %d\n", n );
for ( remap = map->remap; remap->infont!=-1; ++remap )
fprintf(sfd, "Remap: %x %x %d\n", (int) remap->firstenc, (int) remap->lastenc, (int) remap->infont );
}
if ( sf->display_size!=0 )
fprintf( sfd, "DisplaySize: %d\n", sf->display_size );
if ( sf->display_layer!=ly_fore )
fprintf( sfd, "DisplayLayer: %d\n", sf->display_layer );
fprintf( sfd, "AntiAlias: %d\n", sf->display_antialias );
fprintf( sfd, "FitToEm: %d\n", sf->display_bbsized );
if ( sf->extrema_bound!=0 )
fprintf( sfd, "ExtremaBound: %d\n", sf->extrema_bound );
if ( sf->width_separation!=0 )
fprintf( sfd, "WidthSeparation: %d\n", sf->width_separation );
{
int rc, cc, te;
if ( (te = FVWinInfo(sf->fv,&cc,&rc))!= -1 )
fprintf( sfd, "WinInfo: %d %d %d\n", te, cc, rc );
else if ( sf->top_enc!=-1 )
fprintf( sfd, "WinInfo: %d %d %d\n", sf->top_enc, sf->desired_col_cnt,
sf->desired_row_cnt);
}
if ( sf->onlybitmaps!=0 )
fprintf( sfd, "OnlyBitmaps: %d\n", sf->onlybitmaps );
if ( sf->private!=NULL )
SFDDumpPrivate(sfd,sf->private);
#if HANYANG
if ( sf->rules!=NULL )
SFDDumpCompositionRules(sfd,sf->rules);
#endif
if ( sf->grid.splines!=NULL ) {
if ( sf->grid.order2 )
fprintf(sfd, "GridOrder2: %d\n", sf->grid.order2 );
fprintf(sfd, "Grid\n" );
SFDDumpSplineSet(sfd,sf->grid.splines);
}
if ( sf->texdata.type!=tex_unset ) {
fprintf(sfd, "TeXData: %d %d", (int) sf->texdata.type, (int) ((sf->design_size<<19)+2)/5 );
for ( i=0; i<22; ++i )
fprintf(sfd, " %d", (int) sf->texdata.params[i]);
putc('\n',sfd);
}
if ( sf->anchor!=NULL ) {
AnchorClass *an;
fprintf(sfd, "AnchorClass2: ");
for ( an=sf->anchor; an!=NULL; an=an->next ) {
SFDDumpUTF7Str(sfd,an->name);
putc(' ',sfd);
if ( an->subtable!=NULL ) {
SFDDumpUTF7Str(sfd,an->subtable->subtable_name);
putc(' ',sfd);
}
else
fprintf(sfd, "\"\" ");
}
putc('\n',sfd);
}
if ( sf->subfontcnt!=0 ) {
if ( todir ) {
for ( i=0; i<sf->subfontcnt; ++i ) {
char *subfont = malloc(strlen(dirname)+1+strlen(sf->subfonts[i]->fontname)+20);
char *fontprops;
FILE *ssfd;
sprintf( subfont,"%s/%s" SUBFONT_EXT, dirname, sf->subfonts[i]->fontname );
GFileMkDir(subfont);
fontprops = malloc(strlen(subfont)+strlen("/" FONT_PROPS)+1);
strcpy(fontprops,subfont); strcat(fontprops,"/" FONT_PROPS);
ssfd = fopen( fontprops,"w");
if ( ssfd!=NULL ) {
err |= SFD_Dump(ssfd,sf->subfonts[i],map,NULL,todir,subfont);
if ( ferror(ssfd) ) err = true;
if ( fclose(ssfd)) err = true;
} else
err = true;
free(fontprops);
free(subfont);
}
} else {
int max;
for ( i=max=0; i<sf->subfontcnt; ++i )
if ( max<sf->subfonts[i]->glyphcnt )
max = sf->subfonts[i]->glyphcnt;
fprintf(sfd, "BeginSubFonts: %d %d\n", sf->subfontcnt, max );
for ( i=0; i<sf->subfontcnt; ++i )
SFD_Dump(sfd,sf->subfonts[i],map,NULL,false, NULL);
fprintf(sfd, "EndSubFonts\n" );
}
} else {
int enccount = map->enccount;
if ( sf->cidmaster!=NULL ) {
realcnt = -1;
enccount = sf->glyphcnt;
} else {
realcnt = 0;
for ( i=0; i<sf->glyphcnt; ++i ) if ( !SFDOmit(sf->glyphs[i]) )
++realcnt;
if ( realcnt!=sf->glyphcnt ) {
newgids = malloc(sf->glyphcnt*sizeof(int));
realcnt = 0;
for ( i=0; i<sf->glyphcnt; ++i )
if ( SFDOmit(sf->glyphs[i]) )
newgids[i] = -1;
else
newgids[i] = realcnt++;
}
}
if ( !todir )
fprintf(sfd, "BeginChars: %d %d\n",
enccount<map->enc->char_cnt? map->enc->char_cnt : enccount,
realcnt );
for ( i=0; i<sf->glyphcnt; ++i ) {
if ( !SFDOmit(sf->glyphs[i]) ) {
if ( !todir )
SFDDumpChar(sfd,sf->glyphs[i],map,newgids,todir,1);
else {
char *glyphfile = malloc(strlen(dirname)+2*strlen(sf->glyphs[i]->name)+20);
FILE *gsfd;
appendnames(glyphfile,dirname,"/",sf->glyphs[i]->name,GLYPH_EXT );
gsfd = fopen(glyphfile,"w");
if ( gsfd!=NULL ) {
SFDDumpChar(gsfd,sf->glyphs[i],map,newgids,todir,1);
if ( ferror(gsfd)) err = true;
if ( fclose(gsfd)) err = true;
} else
err = true;
free(glyphfile);
}
}
ff_progress_next();
}
if ( !todir )
fprintf(sfd, "EndChars\n" );
}
if ( sf->bitmaps!=NULL )
ff_progress_change_line2(_("Saving Bitmaps"));
for ( bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
if ( todir ) {
char *strike = malloc(strlen(dirname)+1+20+20);
char *strikeprops;
FILE *ssfd;
sprintf( strike,"%s/%d" STRIKE_EXT, dirname, bdf->pixelsize );
GFileMkDir(strike);
strikeprops = malloc(strlen(strike)+strlen("/" STRIKE_PROPS)+1);
strcpy(strikeprops,strike); strcat(strikeprops,"/" STRIKE_PROPS);
ssfd = fopen( strikeprops,"w");
if ( ssfd!=NULL ) {
err |= SFDDumpBitmapFont(ssfd,bdf,map,newgids,todir,strike);
if ( ferror(ssfd) ) err = true;
if ( fclose(ssfd)) err = true;
} else
err = true;
free(strikeprops);
free(strike);
} else
SFDDumpBitmapFont(sfd,bdf,map,newgids,todir,dirname);
}
fprintf(sfd, sf->cidmaster==NULL?"EndSplineFont\n":"EndSubSplineFont\n" );
free(newgids);
return( err );
}
static int SFD_MIDump(SplineFont *sf,EncMap *map,char *dirname, int mm_pos) {
char *instance = malloc(strlen(dirname)+1+10+20);
char *fontprops;
FILE *ssfd;
int err = false;
/* I'd like to use the font name, but the order of the instances is */
/* crucial and I must enforce an ordering on them */
sprintf( instance,"%s/mm%d" INSTANCE_EXT, dirname, mm_pos );
GFileMkDir(instance);
fontprops = malloc(strlen(instance)+strlen("/" FONT_PROPS)+1);
strcpy(fontprops,instance); strcat(fontprops,"/" FONT_PROPS);
ssfd = fopen( fontprops,"w");
if ( ssfd!=NULL ) {
err |= SFD_Dump(ssfd,sf,map,NULL,true,instance);
if ( ferror(ssfd) ) err = true;
if ( fclose(ssfd)) err = true;
} else
err = true;
free(fontprops);
free(instance);
return( err );
}
static int SFD_MMDump(FILE *sfd,SplineFont *sf,EncMap *map,EncMap *normal,
int todir, char *dirname) {
MMSet *mm = sf->mm;
int max, i, j;
int err = false;
fprintf( sfd, "MMCounts: %d %d %d %d\n", mm->instance_count, mm->axis_count,
mm->apple, mm->named_instance_count );
fprintf( sfd, "MMAxis:" );
for ( i=0; i<mm->axis_count; ++i )
fprintf( sfd, " %s", mm->axes[i]);
putc('\n',sfd);
fprintf( sfd, "MMPositions:" );
for ( i=0; i<mm->axis_count*mm->instance_count; ++i )
fprintf( sfd, " %g", (double) mm->positions[i]);
putc('\n',sfd);
fprintf( sfd, "MMWeights:" );
for ( i=0; i<mm->instance_count; ++i )
fprintf( sfd, " %g", (double) mm->defweights[i]);
putc('\n',sfd);
for ( i=0; i<mm->axis_count; ++i ) {
fprintf( sfd, "MMAxisMap: %d %d", i, mm->axismaps[i].points );
for ( j=0; j<mm->axismaps[i].points; ++j )
fprintf( sfd, " %g=>%g", (double) mm->axismaps[i].blends[j], (double) mm->axismaps[i].designs[j]);
fputc('\n',sfd);
SFDDumpMacName(sfd,mm->axismaps[i].axisnames);
}
if ( mm->cdv!=NULL ) {
fprintf( sfd, "MMCDV:\n" );
fputs(mm->cdv,sfd);
fprintf( sfd, "\nEndMMSubroutine\n" );
}
if ( mm->ndv!=NULL ) {
fprintf( sfd, "MMNDV:\n" );
fputs(mm->ndv,sfd);
fprintf( sfd, "\nEndMMSubroutine\n" );
}
for ( i=0; i<mm->named_instance_count; ++i ) {
fprintf( sfd, "MMNamedInstance: %d ", i );
for ( j=0; j<mm->axis_count; ++j )
fprintf( sfd, " %g", (double) mm->named_instances[i].coords[j]);
fputc('\n',sfd);
SFDDumpMacName(sfd,mm->named_instances[i].names);
}
if ( todir ) {
for ( i=0; i<mm->instance_count; ++i )
err |= SFD_MIDump(mm->instances[i],map,dirname,i+1);
err |= SFD_MIDump(mm->normal,map,dirname,0);
} else {
for ( i=max=0; i<mm->instance_count; ++i )
if ( max<mm->instances[i]->glyphcnt )
max = mm->instances[i]->glyphcnt;
fprintf(sfd, "BeginMMFonts: %d %d\n", mm->instance_count+1, max );
for ( i=0; i<mm->instance_count; ++i )
SFD_Dump(sfd,mm->instances[i],map,normal,todir,dirname);
SFD_Dump(sfd,mm->normal,map,normal,todir,dirname);
}
fprintf(sfd, "EndMMFonts\n" );
return( err );
}
static int SFDDump(FILE *sfd,SplineFont *sf,EncMap *map,EncMap *normal,
int todir, char *dirname) {
int i, realcnt;
BDFFont *bdf;
int err = false;
realcnt = sf->glyphcnt;
if ( sf->subfontcnt!=0 ) {
for ( i=0; i<sf->subfontcnt; ++i )
if ( realcnt<sf->subfonts[i]->glyphcnt )
realcnt = sf->subfonts[i]->glyphcnt;
}
for ( i=0, bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++i );
ff_progress_start_indicator(10,_("Saving..."),_("Saving Spline Font Database"),_("Saving Outlines"),
realcnt,i+1);
ff_progress_enable_stop(false);
double version = 3.1;
if( !UndoRedoLimitToSave )
version = 3.0;
fprintf(sfd, "SplineFontDB: %.1f\n", version );
if ( sf->mm != NULL )
err = SFD_MMDump(sfd,sf->mm->normal,map,normal,todir,dirname);
else
err = SFD_Dump(sfd,sf,map,normal,todir,dirname);
ff_progress_end_indicator();
return( err );
}
static void SFDirClean(char *filename) {
DIR *dir;
struct dirent *ent;
char *buffer, *pt;
unlink(filename); /* Just in case it's a normal file, it shouldn't be, but just in case... */
dir = opendir(filename);
if ( dir==NULL )
return;
buffer = malloc(strlen(filename)+1+NAME_MAX+1);
while ( (ent = readdir(dir))!=NULL ) {
if ( strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0 )
continue;
pt = strrchr(ent->d_name,EXT_CHAR);
if ( pt==NULL )
continue;
sprintf( buffer,"%s/%s", filename, ent->d_name );
if ( strcmp(pt,".props")==0 ||
strcmp(pt,GLYPH_EXT)==0 ||
strcmp(pt,BITMAP_EXT)==0 )
unlink( buffer );
else if ( strcmp(pt,STRIKE_EXT)==0 ||
strcmp(pt,SUBFONT_EXT)==0 ||
strcmp(pt,INSTANCE_EXT)==0 )
SFDirClean(buffer);
/* If there are filenames we don't recognize, leave them. They might contain version control info */
}
free(buffer);
closedir(dir);
}
static void SFFinalDirClean(char *filename) {
DIR *dir;
struct dirent *ent;
char *buffer, *markerfile, *pt;
/* we did not unlink sub-directories in case they contained version control */
/* files. We did remove all our files from them, however. If the user */
/* removed a bitmap strike or a cid-subfont those sub-dirs will now be */
/* empty. If the user didn't remove them then they will contain our marker */
/* files. So if we find a subdir with no marker files in it, remove it */
dir = opendir(filename);
if ( dir==NULL )
return;
buffer = malloc(strlen(filename)+1+NAME_MAX+1);
markerfile = malloc(strlen(filename)+2+2*NAME_MAX+1);
while ( (ent = readdir(dir))!=NULL ) {
if ( strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0 )
continue;
pt = strrchr(ent->d_name,EXT_CHAR);
if ( pt==NULL )
continue;
sprintf( buffer,"%s/%s", filename, ent->d_name );
if ( strcmp(pt,".strike")==0 ||
strcmp(pt,SUBFONT_EXT)==0 ||
strcmp(pt,INSTANCE_EXT)==0 ) {
if ( strcmp(pt,".strike")==0 )
sprintf( markerfile,"%s/" STRIKE_PROPS, buffer );
else
sprintf( markerfile,"%s/" FONT_PROPS, buffer );
if ( !GFileExists(markerfile)) {
GFileRemove(buffer, false);
}
}
}
free(buffer);
free(markerfile);
closedir(dir);
}
int SFDWrite(char *filename,SplineFont *sf,EncMap *map,EncMap *normal,int todir) {
FILE *sfd;
int i, gc;
char *tempfilename = filename;
int err = false;
if ( todir ) {
SFDirClean(filename);
GFileMkDir(filename); /* this will fail if directory already exists. That's ok */
tempfilename = malloc(strlen(filename)+strlen("/" FONT_PROPS)+1);
strcpy(tempfilename,filename); strcat(tempfilename,"/" FONT_PROPS);
}
if ( !todir && strstr(filename,"://")!=NULL )
sfd = tmpfile();
else
sfd = fopen(tempfilename,"w");
if ( tempfilename!=filename ) free(tempfilename);
if ( sfd==NULL )
return( 0 );
locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
if ( sf->cidmaster!=NULL ) {
sf=sf->cidmaster;
gc = 1;
for ( i=0; i<sf->subfontcnt; ++i )
if ( sf->subfonts[i]->glyphcnt > gc )
gc = sf->subfonts[i]->glyphcnt;
map = EncMap1to1(gc);
err = SFDDump(sfd,sf,map,NULL,todir,filename);
EncMapFree(map);
} else
err = SFDDump(sfd,sf,map,normal,todir,filename);
switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
if ( ferror(sfd) ) err = true;
if ( !err && !todir && strstr(filename,"://")!=NULL )
err = !URLFromFile(filename,sfd);
if ( fclose(sfd) ) err = true;
if ( todir )
SFFinalDirClean(filename);
return( !err );
}
int SFDDoesAnyBackupExist(char* filename)
{
char path[PATH_MAX];
int idx = 1;
snprintf( path, PATH_MAX, "%s-%02d", filename, idx );
return GFileExists(path);
}
/**
* Handle creation of potential implicit revisions when saving.
*
* If s2d is set then we are saving to an sfdir and no revisions are
* created.
*
* If localRevisionsToRetain == 0 then no revisions are made.
*
* If localRevisionsToRetain > 0 then it is taken as an explict number
* of revisions to make, and revisions are made
*
* If localRevisionsToRetain == -1 then it is "not set".
* In that case, revisions are only made if there are already revisions
* for the locfilename.
*
*/
int SFDWriteBakExtended(char* locfilename,
SplineFont *sf,EncMap *map,EncMap *normal,
int s2d,
int localRevisionsToRetain )
{
int rc = 0;
if( s2d )
{
rc = SFDWrite(locfilename,sf,map,normal,s2d);
return rc;
}
int cacheRevisionsToRetain = prefRevisionsToRetain;
sf->save_to_dir = s2d;
if( localRevisionsToRetain < 0 )
{
// If there are no backups, then don't start creating any
if( !SFDDoesAnyBackupExist(sf->filename))
prefRevisionsToRetain = 0;
}
else
{
prefRevisionsToRetain = localRevisionsToRetain;
}
rc = SFDWriteBak( locfilename, sf, map, normal );
prefRevisionsToRetain = cacheRevisionsToRetain;
return rc;
}
int SFDWriteBak(char *filename,SplineFont *sf,EncMap *map,EncMap *normal) {
char *buf=0, *buf2=NULL;
int ret;
if ( sf->save_to_dir )
{
ret = SFDWrite(filename,sf,map,normal,true);
return(ret);
}
if ( sf->cidmaster!=NULL )
sf=sf->cidmaster;
buf = malloc(strlen(filename)+10);
if ( sf->compression!=0 )
{
buf2 = malloc(strlen(filename)+10);
strcpy(buf2,filename);
strcat(buf2,compressors[sf->compression-1].ext);
strcpy(buf,buf2);
strcat(buf,"~");
if ( rename(buf2,buf)==0 )
sf->backedup = bs_backedup;
}
else
{
sf->backedup = bs_dontknow;
if( prefRevisionsToRetain )
{
char path[PATH_MAX];
char pathnew[PATH_MAX];
int idx = 0;
snprintf( path, PATH_MAX, "%s", filename );
snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );
(void)rename( path, pathnew );
for( idx=prefRevisionsToRetain; idx > 0; idx-- )
{
snprintf( path, PATH_MAX, "%s-%02d", filename, idx-1 );
snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );
int rc = rename( path, pathnew );
if( !idx && !rc )
sf->backedup = bs_backedup;
}
idx = prefRevisionsToRetain+1;
snprintf( path, PATH_MAX, "%s-%02d", filename, idx );
unlink(path);
}
}
free(buf);
ret = SFDWrite(filename,sf,map,normal,false);
if ( ret && sf->compression!=0 ) {
unlink(buf2);
buf = malloc(strlen(filename)+40);
sprintf( buf, "%s %s", compressors[sf->compression-1].recomp, filename );
if ( system( buf )!=0 )
sf->compression = 0;
free(buf);
}
free(buf2);
return( ret );
}
/* ********************************* INPUT ********************************** */
#include "sfd1.h"
char *getquotedeol(FILE *sfd) {
char *pt, *str, *end;
int ch;
pt = str = malloc(101); end = str+100;
while ( isspace(ch = nlgetc(sfd)) && ch!='\r' && ch!='\n' );
while ( ch!='\n' && ch!='\r' && ch!=EOF ) {
if ( ch=='\\' ) {
/* We can't use nlgetc() here, because it would misinterpret */
/* double backslash at the end of line. Multiline strings, */
/* broken with backslash + newline, are just handled above. */
ch = getc(sfd);
if ( ch=='n' ) ch='\n';
/* else if ( ch=='\\' ) ch=='\\'; */ /* second backslash of '\\' */
/* FontForge doesn't write other escape sequences in this context. */
/* So any other value of ch is assumed impossible. */
}
if ( pt>=end ) {
pt = realloc(str,end-str+101);
end = pt+(end-str)+100;
str = pt;
pt = end-100;
}
*pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
/* these strings should be in utf8 now, but some old sfd files might have */
/* latin1. Not a severe problems because they SHOULD be in ASCII. So any */
/* non-ascii strings are erroneous anyway */
if ( !utf8_valid(str) ) {
pt = latin1_2_utf8_copy(str);
free(str);
str = pt;
}
return( str );
}
static int geteol(FILE *sfd, char *tokbuf) {
char *pt=tokbuf, *end = tokbuf+2000-2; int ch;
while ( isspace(ch = nlgetc(sfd)) && ch!='\r' && ch!='\n' );
while ( ch!='\n' && ch!='\r' && ch!=EOF ) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}
void visitSFDFragment( FILE *sfd, SplineFont *sf,
visitSFDFragmentFunc ufunc, void* udata )
{
int eof;
char tok[2000];
while ( 1 ) {
if ( (eof = getname(sfd,tok))!=1 ) {
if ( eof==-1 )
break;
geteol(sfd,tok);
continue;
}
ufunc( sfd, tok, sf, udata );
}
}
static int getprotectedname(FILE *sfd, char *tokbuf) {
char *pt=tokbuf, *end = tokbuf+100-2; int ch;
while ( (ch = nlgetc(sfd))==' ' || ch=='\t' );
while ( ch!=EOF && !isspace(ch) && ch!='[' && ch!=']' && ch!='{' && ch!='}' && ch!='<' && ch!='%' ) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
if ( pt==tokbuf && ch!=EOF )
*pt++ = ch;
else
ungetc(ch,sfd);
*pt='\0';
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}
int getname(FILE *sfd, char *tokbuf) {
int ch;
while ( isspace(ch = nlgetc(sfd)));
ungetc(ch,sfd);
return( getprotectedname(sfd,tokbuf));
}
static uint32 gettag(FILE *sfd) {
int ch, quoted;
uint32 tag;
while ( (ch=nlgetc(sfd))==' ' );
if ( (quoted = (ch=='\'')) ) ch = nlgetc(sfd);
tag = (ch<<24)|(nlgetc(sfd)<<16);
tag |= nlgetc(sfd)<<8;
tag |= nlgetc(sfd);
if ( quoted ) (void) nlgetc(sfd);
return( tag );
}
static int getint(FILE *sfd, int *val) {
char tokbuf[100]; int ch;
char *pt=tokbuf, *end = tokbuf+100-2;
while ( isspace(ch = nlgetc(sfd)));
if ( ch=='-' || ch=='+' ) {
*pt++ = ch;
ch = nlgetc(sfd);
}
while ( isdigit(ch)) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
ungetc(ch,sfd);
*val = strtol(tokbuf,NULL,10);
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}
static int getlonglong(FILE *sfd, long long *val) {
char tokbuf[100]; int ch;
char *pt=tokbuf, *end = tokbuf+100-2;
while ( isspace(ch = nlgetc(sfd)));
if ( ch=='-' || ch=='+' ) {
*pt++ = ch;
ch = nlgetc(sfd);
}
while ( isdigit(ch)) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
ungetc(ch,sfd);
*val = strtoll(tokbuf,NULL,10);
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}
static int gethex(FILE *sfd, uint32 *val) {
char tokbuf[100]; int ch;
char *pt=tokbuf, *end = tokbuf+100-2;
while ( isspace(ch = nlgetc(sfd)));
if ( ch=='#' )
ch = nlgetc(sfd);
if ( ch=='-' || ch=='+' ) {
*pt++ = ch;
ch = nlgetc(sfd);
}
if ( ch=='0' ) {
ch = nlgetc(sfd);
if ( ch=='x' || ch=='X' )
ch = nlgetc(sfd);
else {
ungetc(ch,sfd);
ch = '0';
}
}
while ( isdigit(ch) || (ch>='a' && ch<='f') || (ch>='A' && ch<='F')) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
ungetc(ch,sfd);
*val = strtoul(tokbuf,NULL,16);
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}
static int gethexints(FILE *sfd, uint32 *val, int cnt) {
int i, ch;
for ( i=0; i<cnt; ++i ) {
if ( i!=0 ) {
ch = nlgetc(sfd);
if ( ch!='.' ) ungetc(ch,sfd);
}
if ( !gethex(sfd,&val[i]))
return( false );
}
return( true );
}
static int getsint(FILE *sfd, int16 *val) {
int val2;
int ret = getint(sfd,&val2);
*val = val2;
return( ret );
}
static int getusint(FILE *sfd, uint16 *val) {
int val2;
int ret = getint(sfd,&val2);
*val = val2;
return( ret );
}
static int getreal(FILE *sfd, real *val) {
char tokbuf[100];
int ch;
char *pt=tokbuf, *end = tokbuf+100-2, *nend;
while ( isspace(ch = nlgetc(sfd)));
if ( ch!='e' && ch!='E' ) /* real's can't begin with exponants */
while ( isdigit(ch) || ch=='-' || ch=='+' || ch=='e' || ch=='E' || ch=='.' || ch==',' ) {
if ( pt<end ) *pt++ = ch;
ch = nlgetc(sfd);
}
*pt='\0';
ungetc(ch,sfd);
*val = strtod(tokbuf,&nend);
/* Beware of different locals! */
if ( *nend!='\0' ) {
if ( *nend=='.' )
*nend = ',';
else if ( *nend==',' )
*nend = '.';
*val = strtod(tokbuf,&nend);
}
return( pt!=tokbuf && *nend=='\0'?1:ch==EOF?-1: 0 );
}
/* Don't use nlgetc here. We carefully control newlines when dumping in 85 */
/* but backslashes can occur at end of line. */
static int Dec85(struct enc85 *dec) {
int ch1, ch2, ch3, ch4, ch5;
unsigned int val;
if ( dec->pos<0 ) {
while ( isspace(ch1=getc(dec->sfd)));
if ( ch1=='z' ) {
dec->sofar[0] = dec->sofar[1] = dec->sofar[2] = dec->sofar[3] = 0;
dec->pos = 3;
} else {
while ( isspace(ch2=getc(dec->sfd)));
while ( isspace(ch3=getc(dec->sfd)));
while ( isspace(ch4=getc(dec->sfd)));
while ( isspace(ch5=getc(dec->sfd)));
val = ((((ch1-'!')*85+ ch2-'!')*85 + ch3-'!')*85 + ch4-'!')*85 + ch5-'!';
dec->sofar[3] = val>>24;
dec->sofar[2] = val>>16;
dec->sofar[1] = val>>8;
dec->sofar[0] = val;
dec->pos = 3;
}
}
return( dec->sofar[dec->pos--] );