UFO Anchor points

View: New views
2 Messages — Rating Filter:   Alert me  

UFO Anchor points

by Martin Hosken :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear All,

The enclosed patch seeks to address UFO import and export of Anchor points. RoboFab handles anchors by emitting them as a single point contour (with just a single move type point) giving that point a name.

FontForge doesn't really have the concept of an arbitrary anchor point. All anchors must be associated via a AnchorClass to a lookup. While this is great if you are doing all your OT work in fontforge, it is problematic if you are viewing fontforge purely as a design tool which links to external tools, and are viewing anchors merely as something else a designer puts into their design.

To get around this philosophical clash, this patch programs the UFO import to read any anchor beginning with _ as a mark type anchor (as is the fontlab convention). All other anchors are considered simply basechar anchors. We then create a hardwired named lookup (_holdAnchors) of type mark2base with a subtable (_someAnchors) and put all the AnchorClasses we need in there.

I don't necessarily think this is the best way to address this issue. But it is one way that seems to work. I hope others can come up with a better approach to solving this.

The output routine outputs all anchors as single point contours as per RoboFab.

There are some spurious changes due to unexpanding the code layout to ensure appropriate tabbing. I don't know how strict the coding culture is on this code.

Yours,
Martin

[fontforge-hg_rev5079.patch]

# HG changeset patch
# User Martin Hosken <martin_hosken@...>
# Date 1254974960 -25200
# Branch mhosken
# Node ID 8b00f4e32f2888b3e175ed1ebb7c0d63ce98e750
# Parent  00fa00d5781362cb6fe7d97a7c600f9a85b17259
Add anchor support to UFO

diff -r 00fa00d57813 -r 8b00f4e32f28 fontforge/ufo.c
--- a/fontforge/ufo.c Thu Oct 08 08:22:55 2009 +0700
+++ b/fontforge/ufo.c Thu Oct 08 11:09:20 2009 +0700
@@ -223,6 +223,7 @@
     SplinePoint *sp;
     RefChar *ref;
     int err;
+    AnchorPoint *ap;
 
     if ( glif==NULL )
 return( false );
@@ -258,6 +259,16 @@
  fprintf( glif, " yOffset=\"%g\"", (double) ref->transform[5] );
     fprintf( glif, "/>\n" );
  }
+ for ( ap = sc->anchor; ap!=NULL; ap=ap->next ) {
+    fprintf( glif, "    <contour>\n" );
+    if ( ap->type==at_mark )
+ fprintf( glif, "        <point x=\"%g\" y=\"%g\" type\"move\" name=\"_%s\"/>\n",
+    (double) ap->me.x, (double) ap->me.y, ap->anchor->name );
+    else
+ fprintf( glif, "        <point x=\"%g\" y=\"%g\" type\"move\" name=\"%s\"/>\n",
+    (double) ap->me.x, (double) ap->me.y, ap->anchor->name );
+    fprintf( glif, "    </contour>\n" );
+ }
  for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
     fprintf( glif, "    <contour>\n" );
     for ( sp=spl->first; sp!=NULL; ) {
@@ -1085,15 +1096,15 @@
     }
     if ( pos!=-88888888 && width!=0 ) {
  h = chunkalloc(sizeof(StemInfo));
-        h->start = pos;
-        h->width = width;
-        if ( width==-20 || width==-21 )
+ h->start = pos;
+ h->width = width;
+ if ( width==-20 || width==-21 )
     h->ghost = true;
  if ( head==NULL )
     head = last = h;
  else {
     last->next = h;
-            last = h;
+    last = h;
  }
     }
  }
@@ -1105,7 +1116,46 @@
 return( head );
 }
 
-static SplineChar *_UFOLoadGlyph(xmlDocPtr doc,char *glifname) {
+static AnchorClass *UFOGetAnchorClass(char *name,SplineFont *sf) {
+    struct lookup_subtable *sub;
+    AnchorClass *ac;
+    OTLookup *otl;
+
+    if ( sf==NULL )
+    return( NULL );
+    for ( ac = sf->anchor; ac!=NULL && strcmp(ac->name,name)!=0; ac = ac->next);
+    if ( ac!=NULL )
+    return( ac );
+    for ( otl = sf->gpos_lookups; otl!=NULL && strcmp(otl->lookup_name,"_holdAnchors")!=0; otl = otl->next);
+    if ( otl==NULL ) {
+ otl = chunkalloc(sizeof(OTLookup));
+ otl->lookup_name=galloc(13);
+ strcpy(otl->lookup_name,"_holdAnchors");
+ otl->lookup_type = gpos_mark2base;
+ otl->next = sf->gpos_lookups;
+ sf->gpos_lookups = otl;
+    }
+    for ( sub = otl->subtables; sub!=NULL && strcmp(sub->subtable_name,"_someAnchors")!=0; sub = sub->next);
+    if ( sub==NULL ) {
+ sub = chunkalloc(sizeof(struct lookup_subtable));
+ sub->subtable_name=galloc(14);
+ strcpy(sub->subtable_name,"_someAnchors");
+ sub->anchor_classes = true;
+ sub->lookup = otl;
+ sub->next = otl->subtables;
+ otl->subtables = sub;
+    }
+    ac = chunkalloc(sizeof(AnchorClass));
+    ac->name = galloc(strlen(name)+1);
+    strcpy(ac->name,name);
+    ac->subtable = sub;
+    ac->type = act_mark;
+    ac->next = sf->anchor;
+    sf->anchor = ac;
+    return( ac );
+}
+
+static SplineChar *_UFOLoadGlyph(xmlDocPtr doc,char *glifname,SplineFont *sf) {
     xmlNodePtr glyph, kids, contour, points;
     SplineChar *sc;
     xmlChar *format, *width, *height, *u;
@@ -1201,7 +1251,7 @@
 
     ss = chunkalloc(sizeof(SplineSet));
     for ( points = contour->children; points!=NULL; points=points->next ) {
- char *xs, *ys, *type;
+ char *xs, *ys, *type, *name;
  double x,y;
  if ( _xmlStrcmp(points->name,(const xmlChar *) "point")!=0 )
     continue;
@@ -1211,36 +1261,55 @@
  if ( xs==NULL || ys == NULL )
     continue;
  x = strtod(xs,NULL); y = strtod(ys,NULL);
- if ( type!=NULL && (strcmp(type,"move")==0 ||
+ name = _xmlGetProp(points,(xmlChar *) "name");
+ /* test for anchor points */
+ if ( name!=NULL && strcmp(type,"move")==0 ) {
+    AnchorPoint *ap = chunkalloc(sizeof(AnchorPoint));
+    if (name[0] == '_') {
+ ap->type = at_mark;
+ ap->anchor = UFOGetAnchorClass(name+1,sf);
+ ap->anchor->has_mark = true;
+    } else {
+ ap->type = at_basechar;
+ ap->anchor = UFOGetAnchorClass(name,sf);
+ ap->anchor->has_base = true;
+    }
+    ap->me.x = x;
+    ap->me.y = y;
+    ap->next = sc->anchor;
+    sc->anchor = ap;
+    open = true;
+    continue;
+ } else if ( type!=NULL && (strcmp(type,"move")==0 ||
     strcmp(type,"line")==0 ||
     strcmp(type,"curve")==0 ||
     strcmp(type,"qcurve")==0 )) {
     sp = SplinePointCreate(x,y);
     if ( strcmp(type,"move")==0 ) {
  open = true;
-        ss->first = ss->last = sp;
+ ss->first = ss->last = sp;
     } else if ( ss->first==NULL ) {
  ss->first = ss->last = sp;
-        memcpy(init,pre,sizeof(pre));
-        initcnt = precnt;
-        if ( strcmp(type,"qcurve")==0 )
+ memcpy(init,pre,sizeof(pre));
+ initcnt = precnt;
+ if ( strcmp(type,"qcurve")==0 )
     wasquad = true;
     } else if ( strcmp(type,"line")==0 ) {
  SplineMake(ss->last,sp,false);
-        ss->last = sp;
+ ss->last = sp;
     } else if ( strcmp(type,"curve")==0 ) {
  wasquad = false;
  if ( precnt==2 ) {
     ss->last->nextcp = pre[0];
-            ss->last->nonextcp = false;
-            sp->prevcp = pre[1];
-            sp->noprevcp = false;
+    ss->last->nonextcp = false;
+    sp->prevcp = pre[1];
+    sp->noprevcp = false;
  } else if ( precnt==1 ) {
     ss->last->nextcp = sp->prevcp = pre[0];
-            ss->last->nonextcp = sp->noprevcp = false;
+    ss->last->nonextcp = sp->noprevcp = false;
  }
  SplineMake(ss->last,sp,false);
-        ss->last = sp;
+ ss->last = sp;
     } else if ( strcmp(type,"qcurve")==0 ) {
  wasquad = true;
  if ( precnt==2 ) {
@@ -1250,12 +1319,12 @@
     SplineMake(ss->last,sp,true);
     ss->last = sp;
  }
-        if ( precnt>=1 ) {
+ if ( precnt>=1 ) {
     ss->last->nextcp = sp->prevcp = pre[precnt-1];
-            ss->last->nonextcp = sp->noprevcp = false;
+    ss->last->nonextcp = sp->noprevcp = false;
  }
  SplineMake(ss->last,sp,true);
-        ss->last = sp;
+ ss->last = sp;
     }
     precnt = 0;
  } else {
@@ -1264,44 +1333,44 @@
  memcpy(init,pre,sizeof(pre));
  initcnt = 1;
  sp = SplinePointCreate((pre[1].x+pre[0].x)/2,(pre[1].y+pre[0].y)/2);
-        sp->nextcp = pre[1];
-        sp->nonextcp = false;
-        if ( ss->first==NULL )
+ sp->nextcp = pre[1];
+ sp->nonextcp = false;
+ if ( ss->first==NULL )
     ss->first = sp;
  else {
     ss->last->nextcp = sp->prevcp = pre[0];
-            ss->last->nonextcp = sp->noprevcp = false;
-            initcnt = 0;
-            SplineMake(ss->last,sp,true);
+    ss->last->nonextcp = sp->noprevcp = false;
+    initcnt = 0;
+    SplineMake(ss->last,sp,true);
  }
-        ss->last = sp;
+ ss->last = sp;
  sp = SplinePointCreate((x+pre[1].x)/2,(y+pre[1].y)/2);
-        sp->prevcp = pre[1];
-        sp->noprevcp = false;
-        SplineMake(ss->last,sp,true);
-        ss->last = sp;
-        pre[0].x = x; pre[0].y = y;
-        precnt = 1;
+ sp->prevcp = pre[1];
+ sp->noprevcp = false;
+ SplineMake(ss->last,sp,true);
+ ss->last = sp;
+ pre[0].x = x; pre[0].y = y;
+ precnt = 1;
  wasquad = true;
     } else if ( wasquad==true && precnt==1 ) {
  sp = SplinePointCreate((x+pre[0].x)/2,(y+pre[0].y)/2);
-        sp->prevcp = pre[0];
-        sp->noprevcp = false;
-        if ( ss->last==NULL ) {
+ sp->prevcp = pre[0];
+ sp->noprevcp = false;
+ if ( ss->last==NULL ) {
     ss->first = sp;
-            memcpy(init,pre,sizeof(pre));
-            initcnt = 1;
+    memcpy(init,pre,sizeof(pre));
+    initcnt = 1;
  } else {
     ss->last->nextcp = sp->prevcp;
-            ss->last->nonextcp = false;
+    ss->last->nonextcp = false;
     SplineMake(ss->last,sp,true);
  }
  ss->last = sp;
-        pre[0].x = x; pre[0].y = y;
+ pre[0].x = x; pre[0].y = y;
     } else if ( precnt<2 ) {
  pre[precnt].x = x;
-        pre[precnt].y = y;
-        ++precnt;
+ pre[precnt].y = y;
+ ++precnt;
     }
  }
  free(xs); free(ys); free(type);
@@ -1318,10 +1387,10 @@
     int i;
     for ( i=0; i<initcnt-1; ++i ) {
  sp = SplinePointCreate((init[i+1].x+init[i].x)/2,(init[i+1].y+init[i].y)/2);
-        sp->prevcp = ss->last->nextcp = init[i];
-        sp->noprevcp = ss->last->nonextcp = false;
-        SplineMake(ss->last,sp,true);
-        ss->last = sp;
+ sp->prevcp = ss->last->nextcp = init[i];
+ sp->noprevcp = ss->last->nonextcp = false;
+ SplineMake(ss->last,sp,true);
+ ss->last = sp;
     }
     ss->last->nextcp = ss->first->prevcp = init[initcnt-1];
     ss->last->nonextcp = ss->first->noprevcp = false;
@@ -1334,11 +1403,13 @@
  SplineMake(ss->last,ss->first,wasquad);
  ss->last = ss->first;
     }
-    if ( last==NULL )
- sc->layers[ly_fore].splines = ss;
-    else
- last->next = ss;
-    last = ss;
+    if ( ss->first!=NULL) {
+ if ( last==NULL )
+    sc->layers[ly_fore].splines = ss;
+ else
+    last->next = ss;
+ last = ss;
+    }
  }
     }
  } else if ( _xmlStrcmp(kids->name,(const xmlChar *) "lib")==0 ) {
@@ -1357,8 +1428,8 @@
     if ( temp!=NULL ) {
  sc->hstem = GlifParseHints(doc,temp,"hhints");
  sc->vstem = GlifParseHints(doc,temp,"vhints");
-        SCGuessHHintInstancesList(sc,ly_fore);
-        SCGuessVHintInstancesList(sc,ly_fore);
+ SCGuessHHintInstancesList(sc,ly_fore);
+ SCGuessVHintInstancesList(sc,ly_fore);
     }
  break;
  }
@@ -1375,7 +1446,7 @@
 return( sc );
 }
 
-static SplineChar *UFOLoadGlyph(char *glifname) {
+static SplineChar *UFOLoadGlyph(char *glifname,SplineFont *sf) {
     xmlDocPtr doc;
 
     doc = _xmlParseFile(glifname);
@@ -1383,7 +1454,7 @@
  LogError( _("Bad glif file %s\n" ), glifname);
 return( NULL );
     }
-return( _UFOLoadGlyph(doc,glifname));
+return( _UFOLoadGlyph(doc,glifname,sf));
 }
 
 
@@ -1451,7 +1522,7 @@
     valname = (char *) _xmlNodeListGetString(doc,value->children,true);
     glyphfname = buildname(glyphdir,valname);
     free(valname);
-    sc = UFOLoadGlyph(glyphfname);
+    sc = UFOLoadGlyph(glyphfname,sf);
     if ( sc!=NULL ) {
  sc->parent = sf;
  if ( sf->glyphcnt>=sf->glyphmax )
@@ -1741,10 +1812,10 @@
     } else if ( strncmp((char *) keyname, "openTypeHhea",12)==0 ) {
  if ( _xmlStrcmp(keyname+12,(xmlChar *) "Ascender")==0 ) {
     sf->pfminfo.hhead_ascent = strtol((char *) valname,&end,10);
-            sf->pfminfo.hheadascent_add = false;
+    sf->pfminfo.hheadascent_add = false;
  } else if ( _xmlStrcmp(keyname+12,(xmlChar *) "Descender")==0 ) {
     sf->pfminfo.hhead_descent = strtol((char *) valname,&end,10);
-            sf->pfminfo.hheaddescent_add = false;
+    sf->pfminfo.hheaddescent_add = false;
  } else if ( _xmlStrcmp(keyname+12,(xmlChar *) "LineGap")==0 )
     sf->pfminfo.linegap = strtol((char *) valname,&end,10);
  free(valname);
@@ -1758,7 +1829,7 @@
  sf->pfminfo.pfmset = true;
  if ( _xmlStrcmp(keyname+11,(xmlChar *) "Panose")==0 ) {
     UFOGetByteArray(sf->pfminfo.panose,sizeof(sf->pfminfo.panose),doc,value);
-            sf->pfminfo.panose_set = true;
+    sf->pfminfo.panose_set = true;
  } else if ( _xmlStrcmp(keyname+11,(xmlChar *) "Type")==0 )
     sf->pfminfo.fstype = UFOGetBits(doc,value);
  else if ( _xmlStrcmp(keyname+11,(xmlChar *) "FamilyClass")==0 ) {
@@ -1772,18 +1843,18 @@
  else if ( _xmlStrcmp(keyname+11,(xmlChar *) "VendorID")==0 )
     memcpy(sf->pfminfo.os2_vendor,valname,4);
  else if ( _xmlStrcmp(keyname+11,(xmlChar *) "TypoAscender")==0 ) {
-            sf->pfminfo.typoascent_add = false;
+    sf->pfminfo.typoascent_add = false;
     sf->pfminfo.os2_typoascent = strtol((char *) valname,&end,10);
  } else if ( _xmlStrcmp(keyname+11,(xmlChar *) "TypoDescender")==0 ) {
-            sf->pfminfo.typodescent_add = false;
+    sf->pfminfo.typodescent_add = false;
     sf->pfminfo.os2_typodescent = strtol((char *) valname,&end,10);
  } else if ( _xmlStrcmp(keyname+11,(xmlChar *) "TypoLineGap")==0 )
     sf->pfminfo.os2_typolinegap = strtol((char *) valname,&end,10);
  else if ( _xmlStrcmp(keyname+11,(xmlChar *) "WinAscent")==0 ) {
-            sf->pfminfo.winascent_add = false;
+    sf->pfminfo.winascent_add = false;
     sf->pfminfo.os2_winascent = strtol((char *) valname,&end,10);
  } else if ( _xmlStrcmp(keyname+11,(xmlChar *) "WinDescent")==0 ) {
-            sf->pfminfo.windescent_add = false;
+    sf->pfminfo.windescent_add = false;
     sf->pfminfo.os2_windescent = strtol((char *) valname,&end,10);
  } else if ( strncmp((char *) keyname+11,"Subscript",9)==0 ) {
     sf->pfminfo.subsuper_set = true;
@@ -1971,7 +2042,7 @@
 return( NULL );
 
     oldloc = setlocale(LC_NUMERIC,"C");
-    sc = _UFOLoadGlyph(doc,filename);
+    sc = _UFOLoadGlyph(doc,filename,NULL);
     setlocale(LC_NUMERIC,oldloc);
 
     if ( sc==NULL )


------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Fontforge-devel mailing list
Fontforge-devel@...
https://lists.sourceforge.net/lists/listinfo/fontforge-devel

Re: UFO Anchor points

by Barry Schwartz-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Martin Hosken <martin_hosken@...> writes:
> FontForge doesn't really have the concept of an arbitrary anchor
> point. All anchors must be associated via a AnchorClass to a
> lookup. While this is great if you are doing all your OT work in
> fontforge, it is problematic if you are viewing fontforge purely as
> a design tool which links to external tools, and are viewing anchors
> merely as something else a designer puts into their design.

Because you started me thinking about it --

There is a tiny little bit of a problem with the way fontforge handles
anchors, in that if you use them just to place accents and don’t want
to output them in the font (which indeed I don't) then you need a
script to generate the font (which in fact is what I do).

The same problem arises in my "spacing by anchors" scripts, where
anchor points are used to mark "generalized sidebearings". These
points are of no use in the generated font and so having them in sfnt
tables is a nuisance.

Admittedly in that case I am making "off-label" use of anchor points,
but using them to place accents is a built-in feature of fontforge and
yet is not really connected to sfnt tables, conceptually -- removing
the anchor points does no harm at all to the accented glyphs.


------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Fontforge-devel mailing list
Fontforge-devel@...
https://lists.sourceforge.net/lists/listinfo/fontforge-devel