Skip to content

Commit

Permalink
Merge pull request #1760 from fontforge/fontforge_ufo_groups_1
Browse files Browse the repository at this point in the history
Add experimental support for U. F. O. groups and group kerns.
  • Loading branch information
frank-trampe committed Oct 6, 2014
2 parents 4097d82 + 51a0b0c commit 9a2bf3a
Show file tree
Hide file tree
Showing 8 changed files with 1,971 additions and 245 deletions.
33 changes: 32 additions & 1 deletion fontforge/autowidth2.c
Expand Up @@ -831,9 +831,40 @@ void AutoKern2BuildClasses(SplineFont *sf,int layer,

if ( kc==NULL )
return;
free(kc->firsts); free(kc->seconds); free(kc->offsets);
// free(kc->firsts); free(kc->seconds); // I think that this forgets to free the contained strings.
if (kc->firsts != NULL) {
int tmppos;
for (tmppos = 0; tmppos < kc->first_cnt; tmppos++)
if (kc->firsts[tmppos] != NULL) { free(kc->firsts); kc->firsts = NULL; }
free(kc->firsts); kc->firsts = NULL;
}
if (kc->seconds != NULL) {
int tmppos;
for (tmppos = 0; tmppos < kc->second_cnt; tmppos++)
if (kc->seconds[tmppos] != NULL) { free(kc->seconds); kc->seconds = NULL; }
free(kc->seconds); kc->seconds = NULL;
}
free(kc->offsets);
free(kc->adjusts);

// Group kerning.
// Specifically, we drop the group kerning stuff if the classes are getting redefined.
if (kc->firsts_names != NULL) {
int tmppos;
for (tmppos = 0; tmppos < kc->first_cnt; tmppos++)
if (kc->firsts_names[tmppos] != NULL) { free(kc->firsts_names); kc->firsts_names = NULL; }
free(kc->firsts_names); kc->firsts_names = NULL;
}
if (kc->seconds_names != NULL) {
int tmppos;
for (tmppos = 0; tmppos < kc->second_cnt; tmppos++)
if (kc->seconds_names[tmppos] != NULL) { free(kc->seconds_names); kc->seconds_names = NULL; }
free(kc->seconds_names); kc->seconds_names = NULL;
}
if (kc->firsts_flags != NULL) { free(kc->firsts_flags); kc->firsts_flags = NULL; }
if (kc->seconds_flags != NULL) { free(kc->seconds_flags); kc->seconds_flags = NULL; }
if (kc->offsets_flags != NULL) { free(kc->offsets_flags); kc->offsets_flags = NULL; }

if ( good_enough==-1 )
good_enough = (sf->ascent+sf->descent)/100.0;

Expand Down
51 changes: 43 additions & 8 deletions fontforge/featurefile.c
Expand Up @@ -400,23 +400,32 @@ return;
putc('>',out);
}

int kernclass_for_feature_file(struct splinefont *sf, struct kernclass *kc, int flags) {
// Note that this is not a complete logical inverse of sister function kernclass_for_groups_plist.
return ((flags & FF_KERNCLASS_FLAG_FEATURE) ||
(!(flags & FF_KERNCLASS_FLAG_NATIVE) && (kc->feature || sf->preferred_kerning != 1)));
}

static void dump_kernclass(FILE *out,SplineFont *sf,struct lookup_subtable *sub) {
int i,j;
KernClass *kc = sub->kc;

// We only export classes and rules here that have not been emitted in groups.plist and kerning.plist.
// The feature file can reference classes from groups.plist, but kerning.plist cannot reference groups from the feature file.

for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL ) {
for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL && kernclass_for_feature_file(sf, kc, kc->firsts_flags[i])) {
fprintf( out, " @kc%d_first_%d = [", sub->subtable_offset, i );
dump_glyphnamelist(out,sf,kc->firsts[i] );
fprintf( out, "];\n" );
}
for ( i=0; i<kc->second_cnt; ++i ) if ( kc->seconds[i]!=NULL ) {
for ( i=0; i<kc->second_cnt; ++i ) if ( kc->seconds[i]!=NULL && kernclass_for_feature_file(sf, kc, kc->seconds_flags[i])) {
fprintf( out, " @kc%d_second_%d = [", sub->subtable_offset, i );
dump_glyphnamelist(out,sf,kc->seconds[i] );
fprintf( out, "];\n" );
}
for ( i=0; i<kc->first_cnt; ++i ) if ( kc->firsts[i]!=NULL ) {
for ( j=0; j<kc->second_cnt; ++j ) if ( kc->seconds[j]!=NULL ) {
if ( kc->offsets[i*kc->second_cnt+j]!=0 )
if ( kc->offsets[i*kc->second_cnt+j]!=0 && kernclass_for_feature_file(sf, kc, kc->offsets_flags[i]) )
fprintf( out, " pos @kc%d_first_%d @kc%d_second_%d %d;\n",
sub->subtable_offset, i,
sub->subtable_offset, j,
Expand Down Expand Up @@ -2303,7 +2312,7 @@ struct parseState {
unsigned int skipping: 1;
SplineFont *sf;
struct scriptlanglist *def_langsyses;
struct glyphclasses *classes;
struct glyphclasses *classes; // TODO: This eventually needs to merge with the SplineFont group storage. For now, it needs to copy from it at first invocation.
struct namedanchor *namedAnchors;
struct namedvalue *namedValueRs;
struct feat_item *sofar;
Expand Down Expand Up @@ -2989,13 +2998,16 @@ static char *fea_ParseGlyphClass(struct parseState *tok) {
char *glyphs = NULL;

if ( tok->type==tk_class ) {
// If the class references another class, just copy that.
glyphs = fea_lookup_class_complain(tok,tok->tokbuf);
} else if ( tok->type!=tk_char || tok->tokbuf[0]!='[' ) {
// If it is not a class, we want a list to parse. Anything else is wrong.
LogError(_("Expected '[' in glyph class definition on line %d of %s"), tok->line[tok->inc_depth], tok->filename[tok->inc_depth] );
++tok->err_count;
return( NULL );
} else {
char *contents = NULL;
// Start parsing the list.
char *contents = NULL; // This is a temporary buffer used for each cycle below.
int cnt=0, max=0;
int last_val, range_type, range_len;
char last_glyph[MAXT+1];
Expand All @@ -3006,8 +3018,9 @@ return( NULL );
for (;;) {
fea_ParseTok(tok);
if ( tok->type==tk_char && tok->tokbuf[0]==']' )
break;
break; // End of list.
if ( tok->type==tk_class ) {
// Stash the entire contents of the referenced class for inclusion in this class (later).
contents = fea_lookup_class_complain(tok,tok->tokbuf);
last_val=-1; last_glyph[0] = '\0';
} else if ( tok->type==tk_cid ) {
Expand All @@ -3017,6 +3030,7 @@ return( NULL );
strcpy(last_glyph,tok->tokbuf); last_val = -1;
contents = fea_glyphname_validate(tok,tok->tokbuf);
} else if ( tok->type==tk_char && tok->tokbuf[0]=='-' ) {
// It's a range extending from the previous token.
fea_ParseTok(tok);
if ( last_val!=-1 && tok->type==tk_cid ) {
if ( last_val>=tok->value ) {
Expand Down Expand Up @@ -3235,12 +3249,12 @@ static void fea_ParseGlyphClassDef(struct parseState *tok) {
return;
}
fea_ParseTok(tok);
contents = fea_ParseGlyphClass(tok);
contents = fea_ParseGlyphClass(tok); // Make a list of referenced glyphs.
if ( contents==NULL ) {
fea_skip_to_semi(tok);
return;
}
fea_AddClassDef(tok,classname,copy(contents));
fea_AddClassDef(tok,classname,copy(contents)); // Put the list into a class.
fea_end_statement(tok);
}

Expand Down Expand Up @@ -7214,6 +7228,26 @@ static void fea_NameLookups(struct parseState *tok) {
FVRefreshAll(sf);
}

void CopySplineFontGroupsForFeatureFile(SplineFont *sf, struct parseState* tok) {
struct ff_glyphclasses *ff_current = sf->groups;
struct glyphclasses *feature_current = tok->classes;
// It may be useful to run this multiple times, appending to an existing list, so we traverse to the end.
while (feature_current != NULL && feature_current->next != NULL) feature_current = feature_current->next;
struct glyphclasses *feature_tmp = NULL;
while (ff_current != NULL) {
// Only groups with feature-compatible names get copied.
if (ff_current->classname != NULL && ff_current->classname[0] == '@') {
feature_tmp = calloc(1, sizeof(struct glyphclasses));
feature_tmp->classname = copy(ff_current->classname);
feature_tmp->glyphs = copy(ff_current->glyphs);
if (feature_current != NULL) feature_current->next = feature_tmp;
else tok->classes = feature_tmp;
feature_current = feature_tmp;
}
ff_current = ff_current->next;
}
}

void SFApplyFeatureFile(SplineFont *sf,FILE *file,char *filename) {
struct parseState tok;
struct glyphclasses *gc, *gcnext;
Expand All @@ -7227,6 +7261,7 @@ void SFApplyFeatureFile(SplineFont *sf,FILE *file,char *filename) {
tok.base = 10;
if ( sf->cidmaster ) sf = sf->cidmaster;
tok.sf = sf;
CopySplineFontGroupsForFeatureFile(sf, &tok);

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.
Expand Down
15 changes: 15 additions & 0 deletions fontforge/lookups.c
Expand Up @@ -2019,6 +2019,21 @@ static KernClass *SF_AddKernClass(struct sfmergecontext *mc,KernClass *kc,
newkc->seconds = ClassCopy(newkc->second_cnt,newkc->seconds);
newkc->offsets = malloc(newkc->first_cnt*newkc->second_cnt*sizeof(int16));
memcpy(newkc->offsets,kc->offsets,newkc->first_cnt*newkc->second_cnt*sizeof(int16));
// We support group kerning as well.
if (newkc->firsts_names) newkc->firsts_names = ClassCopy(newkc->first_cnt,newkc->firsts_names);
if (newkc->seconds_names) newkc->seconds_names = ClassCopy(newkc->second_cnt,newkc->seconds_names);
if (newkc->firsts_flags) {
newkc->firsts_flags = malloc(newkc->first_cnt*sizeof(int));
memcpy(newkc->firsts_flags,kc->firsts_flags,newkc->first_cnt*sizeof(int));
}
if (newkc->seconds_flags) {
newkc->seconds_flags = malloc(newkc->second_cnt*sizeof(int));
memcpy(newkc->seconds_flags,kc->seconds_flags,newkc->second_cnt*sizeof(int));
}
if (newkc->offsets_flags) {
newkc->offsets_flags = malloc(newkc->first_cnt*newkc->second_cnt*sizeof(int));
memcpy(newkc->offsets_flags,kc->offsets_flags,newkc->first_cnt*newkc->second_cnt*sizeof(int));
}
return( newkc );
}

Expand Down
137 changes: 132 additions & 5 deletions fontforge/sfd.c
Expand Up @@ -2414,6 +2414,7 @@ int SFD_DumpSplineFontMetadata( FILE *sfd, SplineFont *sf )
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 );
Expand All @@ -2434,6 +2435,40 @@ int SFD_DumpSplineFontMetadata( FILE *sfd, SplineFont *sf )
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 ) {
Expand Down Expand Up @@ -2536,6 +2571,25 @@ int SFD_DumpSplineFontMetadata( FILE *sfd, SplineFont *sf )
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",
Expand Down Expand Up @@ -7933,11 +7987,14 @@ bool SFD_GetFontMetaData( FILE *sfd,
}
}
else if ( strmatch(tok,"KernClass2:")==0 || strmatch(tok,"VKernClass2:")==0 ||
strmatch(tok,"KernClass:")==0 || strmatch(tok,"VKernClass:")==0 )
strmatch(tok,"KernClass:")==0 || strmatch(tok,"VKernClass:")==0 ||
strmatch(tok,"KernClass3:")==0 || strmatch(tok,"VKernClass3:")==0 )
{
int kernclassversion = 0;
if (tok[9] >= '0' && tok[9] <= '9') kernclassversion = tok[9] - '0';
int temp, classstart=1;
int isv = tok[0]=='V';
int old = strchr(tok,'2')==NULL;
int old = (kernclassversion == 0);

if ( (sf->sfd_version<2)!=old ) {
IError( "Version mixup in Kerning Classes of sfd file." );
Expand Down Expand Up @@ -7966,25 +8023,57 @@ bool SFD_GetFontMetaData( FILE *sfd,
kc->subtable = NULL;
}
}
kc->firsts = malloc(kc->first_cnt*sizeof(char *));
kc->seconds = malloc(kc->second_cnt*sizeof(char *));
kc->offsets = malloc(kc->first_cnt*kc->second_cnt*sizeof(int16));
kc->firsts = calloc(kc->first_cnt,sizeof(char *));
kc->seconds = calloc(kc->second_cnt,sizeof(char *));
kc->offsets = calloc(kc->first_cnt*kc->second_cnt,sizeof(int16));
kc->adjusts = calloc(kc->first_cnt*kc->second_cnt,sizeof(DeviceTable));
if (kernclassversion >= 3) {
kc->firsts_flags = calloc(kc->first_cnt, sizeof(int));
kc->seconds_flags = calloc(kc->second_cnt, sizeof(int));
kc->offsets_flags = calloc(kc->first_cnt*kc->second_cnt, sizeof(int));
kc->firsts_names = calloc(kc->first_cnt, sizeof(char*));
kc->seconds_names = calloc(kc->second_cnt, sizeof(char*));
}
kc->firsts[0] = NULL;
for ( i=classstart; i<kc->first_cnt; ++i ) {
if (kernclassversion < 3) {
getint(sfd,&temp);
kc->firsts[i] = malloc(temp+1); kc->firsts[i][temp] = '\0';
nlgetc(sfd); /* skip space */
fread(kc->firsts[i],1,temp,sfd);
} else {
getint(sfd,&kc->firsts_flags[i]);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
kc->firsts_names[i] = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
kc->firsts[i] = SFDReadUTF7Str(sfd);
if (kc->firsts[i] == NULL) kc->firsts[i] = copy(""); // In certain places, this must be defined.
while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
}
}
kc->seconds[0] = NULL;
for ( i=1; i<kc->second_cnt; ++i ) {
if (kernclassversion < 3) {
getint(sfd,&temp);
kc->seconds[i] = malloc(temp+1); kc->seconds[i][temp] = '\0';
nlgetc(sfd); /* skip space */
fread(kc->seconds[i],1,temp,sfd);
} else {
getint(sfd,&temp);
kc->seconds_flags[i] = temp;
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
kc->seconds_names[i] = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd); if (ch == '\n' || ch == EOF) continue;
kc->seconds[i] = SFDReadUTF7Str(sfd);
if (kc->seconds[i] == NULL) kc->seconds[i] = copy(""); // In certain places, this must be defined.
while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
}
}
for ( i=0; i<kc->first_cnt*kc->second_cnt; ++i ) {
if (kernclassversion >= 3) {
getint(sfd,&temp);
kc->offsets_flags[i] = temp;
}
getint(sfd,&temp);
kc->offsets[i] = temp;
SFDReadDeviceTable(sfd,&kc->adjusts[i]);
Expand Down Expand Up @@ -8032,6 +8121,44 @@ bool SFD_GetFontMetaData( FILE *sfd,
d->lastfp = fpst;
SFDParseChainContext(sfd,sf,fpst,tok,old);
}
else if ( strmatch(tok,"Group:")==0 ) {
struct ff_glyphclasses *grouptmp = calloc(1, sizeof(struct ff_glyphclasses));
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
grouptmp->classname = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
grouptmp->glyphs = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
if (d->lastgroup != NULL) d->lastgroup->next = grouptmp; else sf->groups = grouptmp;
d->lastgroup = grouptmp;
}
else if ( strmatch(tok,"GroupKern:")==0 ) {
int temp = 0;
struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
kerntmp->left = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
kerntmp->right = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
getint(sfd,&temp);
kerntmp->offset = temp;
while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
if (d->lastgroupkern != NULL) d->lastgroupkern->next = kerntmp; else sf->groupkerns = kerntmp;
d->lastgroupkern = kerntmp;
}
else if ( strmatch(tok,"GroupVKern:")==0 ) {
int temp = 0;
struct ff_rawoffsets *kerntmp = calloc(1, sizeof(struct ff_rawoffsets));
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
kerntmp->left = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
kerntmp->right = SFDReadUTF7Str(sfd);
while ((ch=nlgetc(sfd)) == ' '); ungetc(ch, sfd);
getint(sfd,&temp);
kerntmp->offset = temp;
while ((ch=nlgetc(sfd)) == ' ' || ch == '\n'); ungetc(ch, sfd);
if (d->lastgroupvkern != NULL) d->lastgroupvkern->next = kerntmp; else sf->groupvkerns = kerntmp;
d->lastgroupvkern = kerntmp;
}
else if ( strmatch(tok,"MacIndic2:")==0 || strmatch(tok,"MacContext2:")==0 ||
strmatch(tok,"MacLigature2:")==0 || strmatch(tok,"MacSimple2:")==0 ||
strmatch(tok,"MacKern2:")==0 || strmatch(tok,"MacInsert2:")==0 ||
Expand Down

0 comments on commit 9a2bf3a

Please sign in to comment.