Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1054 lines (965 sloc) 32.8 KB
/* Copyright (C) 2009-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 "autowidth2.h"
#include "cvundoes.h"
#include "fontforgevw.h"
#include "fvfonts.h"
#include "splineoverlap.h"
#include "splineutil.h"
#include <math.h>
#include <ustring.h>
#include <utype.h>
#define DENOM_FACTOR_OF_EMSIZE 50.0
#include "edgelist2.h"
static int aw2_bbox_separation(AW_Glyph *g1, AW_Glyph *g2, AW_Data *all) {
int j;
int imin_y, imax_y;
real tot, cnt;
real denom;
/* the goal is to give a weighted average that expresses the visual */
/* separation between two glyphs when they are placed so their bounding */
/* boxes are adjacent. The separation between two rectangles would be 0 */
/* While the separation between "T" and "o" would be fairly large */
/* The trick is to guess a good weighting function. My guess is that */
/* things that look close are more important than those which look far */
/* So "T" and "O" should be dominated by the crossbar of the "T"... */
#if !defined(_NO_PYTHON)
if ( PyFF_GlyphSeparationHook!=NULL )
return( PyFF_GlyphSeparation(g1,g2,all) );
#endif
imin_y = g2->imin_y > g1->imin_y ? g2->imin_y : g1->imin_y;
imax_y = g2->imax_y < g1->imax_y ? g2->imax_y : g1->imax_y;
if ( imax_y < imin_y ) /* no overlap. ie grave and "a" */
return( 0 );
denom = all->denom;
tot = cnt = 0;
for ( j=imin_y ; j<imax_y; ++j ) {
if ( g2->left[j-g2->imin_y] < 32767 && g1->right[j-g1->imin_y] > -32767 ) {
/* beware of gaps such as those in "i" or "aaccute" */
real sep = g2->left[j-g2->imin_y] - g1->right[j-g1->imin_y];
real weight = 1.0/(sep + denom);
weight *= weight;
tot += weight*sep;
cnt += weight;
}
}
if ( cnt!=0 )
tot /= cnt;
return( rint( tot ) );
}
static void aw2_figure_lsb(int right_index, AW_Data *all) {
int i;
AW_Glyph *me, *other;
int lsb, tot;
int *vpt = all->visual_separation + right_index;
lsb=0;
me = &all->glyphs[right_index];
for ( i=0; i<all->gcnt; ++i ) {
other = &all->glyphs[i];
tot = all->desired_separation - vpt[i*all->gcnt] - other->rsb;
if ( tot < all->min_sidebearing )
tot = all->min_sidebearing;
else if ( tot > all->max_sidebearing )
tot = all->max_sidebearing;
lsb += tot;
}
if ( i!=0 )
lsb = (lsb + i/2)/i;
lsb = rint((3 * lsb + me->lsb)/4.0);
me->nlsb = lsb;
}
static void aw2_figure_rsb(int left_index, AW_Data *all) {
int i;
AW_Glyph *me, *other;
int rsb, tot;
int *vpt = all->visual_separation + left_index*all->gcnt;
rsb=0;
me = &all->glyphs[left_index];
for ( i=0; i<all->gcnt; ++i ) {
other = &all->glyphs[i];
tot = all->desired_separation - vpt[i] - other->lsb;
if ( tot < all->min_sidebearing )
tot = all->min_sidebearing;
else if ( tot > all->max_sidebearing )
tot = all->max_sidebearing;
rsb += tot;
}
if ( i!=0 )
rsb = (rsb + i/2)/i;
rsb = rint((3 * rsb + me->rsb)/4.0);
me->nrsb = rsb;
}
static void aw2_figure_all_sidebearing(AW_Data *all) {
int i,j;
AW_Glyph *me, *other;
real transform[6], half;
int width, changed;
uint8 *rsel = calloc(all->fv->map->enccount,sizeof(char));
real denom = (all->sf->ascent + all->sf->descent)/DENOM_FACTOR_OF_EMSIZE;
int ldiff, rdiff;
all->denom = denom;
all->visual_separation = malloc(all->gcnt*all->gcnt*sizeof(int));
for ( i=0; i<all->gcnt; ++i ) {
int *vpt = all->visual_separation + i*all->gcnt;
me = &all->glyphs[i];
for ( j=0; j<all->gcnt; ++j ) {
other = &all->glyphs[j];
vpt[j] = aw2_bbox_separation(me,other,all);
}
}
half = all->desired_separation/2;
for ( i=0; i<all->gcnt; ++i ) {
me = &all->glyphs[i];
me->lsb = me->rsb = half;
}
for ( j=0; j<all->loop_cnt; ++j ) {
for ( i=0; i<all->gcnt; ++i )
aw2_figure_lsb(i,all);
for ( i=0; i<all->gcnt; ++i )
aw2_figure_rsb(i,all);
for ( i=0; i<all->gcnt; ++i ) {
AW_Glyph *me = &all->glyphs[i];
me->rsb = me->nrsb;
me->lsb = me->nlsb;
}
}
free(all->visual_separation); all->visual_separation = NULL;
if ( all->normalize ) {
/* This is the dummy flat edge we added. We want the separation between */
/* two adjacent flat edges to be desired_separation */
me = &all->glyphs[all->gcnt-1];
if ( me->lsb+me->rsb != all->desired_separation && me->sc==NULL ) {
if ( me->lsb+me->rsb!=0 ) {
ldiff = (all->desired_separation-(me->lsb+me->rsb)) * me->lsb/(me->lsb+me->rsb);
} else {
ldiff = all->desired_separation/2;
}
rdiff = (all->desired_separation-(me->lsb+me->rsb)) - ldiff;
for ( i=0; (me = &all->glyphs[i])->sc!=NULL; ++i ) {
me->lsb += ldiff;
me->rsb += rdiff;
}
}
}
transform[0] = transform[3] = 1.0;
transform[1] = transform[2] = transform[5] = 0;
for ( i=0; (me = &all->glyphs[i])->sc!=NULL; ++i ) {
changed = 0;
if ( me->lsb != me->bb.minx ) {
transform[4] = me->lsb-me->bb.minx;
FVTrans(all->fv,me->sc,transform,rsel,false);
changed = true;
}
width = me->lsb + me->rsb + rint(me->bb.maxx - me->bb.minx);
if ( me->sc->width != width ) {
SCPreserveWidth(me->sc);
SCSynchronizeWidth(me->sc,width,me->sc->width,all->fv);
changed = true;
}
if ( changed )
SCCharChangedUpdate(me->sc,ly_none);
}
free(rsel);
}
static int ak2_figure_kern(AW_Glyph *g1, AW_Glyph *g2, AW_Data *all) {
int sep = aw2_bbox_separation(g1,g2,all);
sep += g2->bb.minx + g1->sc->width - g1->bb.maxx;
return( all->desired_separation - sep );
}
static int ak2_figure_touch(AW_Glyph *g1, AW_Glyph *g2, AW_Data *all) {
int j;
int imin_y, imax_y;
real smallest, tot;
imin_y = g2->imin_y > g1->imin_y ? g2->imin_y : g1->imin_y;
imax_y = g2->imax_y < g1->imax_y ? g2->imax_y : g1->imax_y;
if ( imax_y < imin_y ) /* no overlap. ie grave and "a" */
return( - (g2->bb.minx + g1->sc->width - g1->bb.maxx) );
smallest = 0x7fff;
for ( j=imin_y ; j<imax_y; ++j ) {
tot = g2->left[j-g2->imin_y] - g1->right[j-g1->imin_y];
if ( tot<smallest )
smallest = tot;
}
if ( smallest == 0x7fff ) /* Overlaps only in gaps, "i" and something between the base and the dot */
return( - (g2->bb.minx + g1->sc->width - g1->bb.maxx) );
smallest += g2->bb.minx + g1->sc->width - g1->bb.maxx;
return( all->desired_separation - smallest );
}
static int ak2_figure_kernclass(int *class1, int *class2, AW_Data *all) {
int h,i;
real subtot, tot, cnt2;
tot = cnt2 = 0;
for ( h=0; class1[h]!=-1; ++h ) {
AW_Glyph *g1 = &all->glyphs[class1[h]];
for ( i = 0; class2[i]!=-1; ++i ) {
AW_Glyph *g2 = &all->glyphs[class2[i]];
subtot = aw2_bbox_separation(g1,g2,all);
tot += g2->bb.minx + g1->sc->width - g1->bb.maxx + subtot;
++cnt2;
}
}
if ( cnt2==0 )
return( 0 );
if ( cnt2!=0 )
tot /= cnt2;
return( all->desired_separation - tot );
}
static int ak2_figure_touchclass(int *class1, int *class2, AW_Data *all) {
int h,i,j;
int imin_y, imax_y;
real smallest, smaller, tot;
smallest = 0x7fff;
for ( h=0; class1[h]!=-1; ++h ) {
AW_Glyph *g1 = &all->glyphs[class1[h]];
for ( i = 0; class2[i]!=-1; ++i ) {
AW_Glyph *g2 = &all->glyphs[class2[i]];
imin_y = g2->imin_y > g1->imin_y ? g2->imin_y : g1->imin_y;
imax_y = g2->imax_y < g1->imax_y ? g2->imax_y : g1->imax_y;
if ( imax_y < imin_y ) { /* no overlap. ie grave and "a" */
if ( smallest < - (g2->bb.minx + g1->sc->width - g1->bb.maxx) )
smallest = - (g2->bb.minx + g1->sc->width - g1->bb.maxx);
continue;
}
smaller = 0x7fff;
for ( j=imin_y ; j<imax_y; ++j ) {
tot = g2->left[j-g2->imin_y] - g1->right[j-g1->imin_y];
if ( tot<smaller )
smaller = tot;
}
if ( smaller == 0x7fff ) {
if ( smallest < - (g2->bb.minx + g1->sc->width - g1->bb.maxx) )
smallest = - (g2->bb.minx + g1->sc->width - g1->bb.maxx);
continue; /* Overlaps only in gaps */
}
smaller += g2->bb.minx + g1->sc->width - g1->bb.maxx;
if ( smaller<smallest )
smallest = smaller;
}
}
if ( smallest==0x7fff )
return( 0 );
return( all->desired_separation - smallest );
}
static double MonotonicFindY(Monotonic *m,double test,double old_t) {
double tstart, tend, t;
tstart = m->tstart; tend = m->tend;
if ( old_t!=-1 ) {
if ( m->yup )
tstart = old_t;
else
tend = old_t;
}
t = IterateSplineSolve(&m->s->splines[1],tstart,tend,test);
return( t );
}
static void aw2_findedges(AW_Glyph *me, AW_Data *all) {
Monotonic *ms, *m;
real ytop, ybottom;
real x, xmin, xmax;
int i;
double t;
Spline1D *msp;
SplineSet *base;
me->imin_y = floor(me->bb.miny/all->sub_height);
me->imax_y = ceil (me->bb.maxy/all->sub_height);
me->left = malloc((me->imax_y-me->imin_y+1)*sizeof(short));
me->right = malloc((me->imax_y-me->imin_y+1)*sizeof(short));
base = LayerAllSplines(&me->sc->layers[all->layer]);
ms = SSsToMContours(base,over_remove); /* over_remove is an arcane way of saying: Look at all contours, not just selected ones */
LayerUnAllSplines(&me->sc->layers[all->layer]);
ytop = me->imin_y*all->sub_height;
for ( m=ms; m!=NULL; m=m->linked ) {
m->t = -1;
if ( m->b.miny<=ytop ) /* can't be less than, but... */
m->t = MonotonicFindY(m,ytop,-1);
}
for ( i=me->imin_y; i<=me->imax_y; ++i ) {
ybottom = ytop; ytop += all->sub_height;
xmin = 1e10; xmax = -1e10;
for ( m=ms; m!=NULL; m=m->linked ) {
if ( m->b.maxy<ybottom || m->b.miny>ytop || m->b.maxy==m->b.miny )
continue;
if ( (t = m->t)==-1 )
t = MonotonicFindY(m,m->b.miny,-1);
msp = &m->s->splines[0];
if ( t!=-1 ) {
x = ((msp->a*t + msp->b)*t+msp->c)*t+msp->d;
if ( x>xmax ) xmax = x;
if ( x<xmin ) xmin = x;
}
if ( ytop<m->b.maxy )
t = m->t = MonotonicFindY(m,ytop,t);
else {
m->t = -1;
t = MonotonicFindY(m,m->b.maxy,t);
}
if ( t!=-1 ) {
x = ((msp->a*t + msp->b)*t+msp->c)*t+msp->d;
if ( x>xmax ) xmax = x;
if ( x<xmin ) xmin = x;
}
}
if ( xmin>1e9 ) {
/* Glyph might have a gap (as "i" or ":" do) */
me->left[i-me->imin_y] = 32767 /* floor((me->bb.maxx - me->bb.minx)/2)*/;
me->right[i-me->imin_y] = -32767 /* floor(-(me->bb.maxx - me->bb.minx)/2)*/;
} else {
me->left[i-me->imin_y] = floor(xmin - me->bb.minx);
me->right[i-me->imin_y] = floor(xmax - me->bb.maxx); /* This is always non-positive, so floor will give the bigger absolute value */
}
}
FreeMonotonics(ms);
}
static void aw2_dummyedges(AW_Glyph *flat,AW_Data *all) {
int i;
int imin_y = 32000, imax_y = -32000;
if ( all!=NULL ) {
for ( i=0; i<all->gcnt; ++i ) {
AW_Glyph *test = &all->glyphs[i];
if ( test->imin_y < imin_y ) imin_y = test->imin_y;
if ( test->imax_y > imax_y ) imax_y = test->imax_y;
}
if ( imin_y == 32000 ) {
imin_y = floor(-all->sf->descent/all->sub_height);
imax_y = ceil ( all->sf->ascent /all->sub_height);
}
flat->imin_y = imin_y; flat->imax_y = imax_y;
}
flat->left = calloc((flat->imax_y-flat->imin_y+1),sizeof(short));
flat->right = calloc((flat->imax_y-flat->imin_y+1),sizeof(short));
}
static void AWGlyphFree( AW_Glyph *me) {
free(me->left);
free(me->right);
#if !defined(_NO_PYTHON)
FFPy_AWGlyphFree(me);
#endif
}
static void aw2_handlescript(AW_Data *all) {
int i;
AW_Glyph *me;
for ( i=0; (me = &all->glyphs[i])->sc!=NULL; ++i )
aw2_findedges(me,all);
aw2_dummyedges(me,all); ++all->gcnt;
aw2_figure_all_sidebearing(all);
for ( i=0; i<all->gcnt; ++i )
AWGlyphFree( &all->glyphs[i] );
}
SplineChar ***GlyphClassesFromNames(SplineFont *sf,char **classnames,
int class_cnt ) {
SplineChar ***classes = calloc(class_cnt,sizeof(SplineChar **));
int i, pass, clen;
char *end, ch, *pt, *cn;
SplineChar *sc;
for ( i=0; i<class_cnt; ++i ) {
cn = copy(classnames[i]==NULL ? "" : classnames[i]);
for ( pass=0; pass<2; ++pass ) {
clen = 0;
for ( pt = cn; *pt; pt = end+1 ) {
while ( *pt==' ' ) ++pt;
if ( *pt=='\0' )
break;
end = strchr(pt,' ');
if ( end==NULL )
end = pt+strlen(pt);
ch = *end;
if ( pass ) {
*end = '\0';
sc = SFGetChar(sf,-1,pt);
if ( sc!=NULL ) {
classes[i][clen++] = sc;
}
*end = ch;
} else
++clen;
if ( ch=='\0' )
break;
}
if ( pass )
classes[i][clen] = NULL;
else
classes[i] = malloc((clen+1)*sizeof(SplineChar *));
}
if ( cn != NULL ) free( cn ) ; cn = NULL ;
}
return( classes );
}
void AutoWidth2(FontViewBase *fv,int separation,int min_side,int max_side,
int chunk_height, int loop_cnt) {
struct scriptlist {
uint32 script;
AW_Glyph *glyphs;
int gcnt;
} *scripts;
int scnt, smax;
int enc, gid, s, i;
SplineFont *sf = fv->sf;
SplineChar *sc;
AW_Data all;
if ( chunk_height <= 0 )
chunk_height = (sf->ascent + sf->descent)/100;
if ( loop_cnt <= 0 )
loop_cnt = 4;
scnt = 0; smax = 10;
scripts = malloc(smax*sizeof(struct scriptlist));
for ( gid=0; gid<sf->glyphcnt; ++gid ) {
if ( (sc = sf->glyphs[gid])!=NULL )
sc->ticked = false;
}
for ( enc=0; enc<fv->map->enccount; ++enc ) {
if ( fv->selected[enc] && (gid=fv->map->map[enc])!=-1 &&
(sc = sf->glyphs[gid])!=NULL && ! sc->ticked &&
HasUseMyMetrics(sc,fv->active_layer)==NULL ) {
/* If Use My Metrics is set, then we can't change the width (which we grab from a refchar) */
uint32 script;
script = SCScriptFromUnicode(sc);
for ( s=0; s<scnt; ++s ) {
if ( scripts[s].script == script )
break;
}
if ( s==scnt ) {
if ( scnt>=smax )
scripts = realloc(scripts,(smax+=10)*sizeof(struct scriptlist));
memset(&scripts[scnt],0,sizeof(struct scriptlist));
scripts[scnt].script = script;
scripts[scnt].glyphs = calloc(sf->glyphcnt+1,sizeof(AW_Glyph));
++scnt;
}
i = scripts[s].gcnt;
scripts[s].glyphs[i].sc = sc;
SplineCharLayerFindBounds(sc,fv->active_layer,&scripts[s].glyphs[i].bb);
if ( scripts[s].glyphs[i].bb.minx<-16000 || scripts[s].glyphs[i].bb.maxx>16000 ||
scripts[s].glyphs[i].bb.miny<-16000 || scripts[s].glyphs[i].bb.maxy>16000 )
ff_post_notice(_("Glyph too big"),_("%s has a bounding box which is too big for this algorithm to work. Ignored."),sc->name);
else
++scripts[s].gcnt;
}
}
memset(&all,0,sizeof(all));
all.sf = sf;
all.fv = fv;
all.layer = fv->active_layer;
all.normalize = true;
all.sub_height = chunk_height;
all.loop_cnt = loop_cnt;
all.desired_separation = separation;
all.min_sidebearing = min_side;
all.max_sidebearing = max_side;
for ( s=0; s<scnt; ++s ) {
memset(&scripts[s].glyphs[scripts[s].gcnt], 0, sizeof(AW_Glyph));
all.glyphs = scripts[s].glyphs;
all.gcnt = scripts[s].gcnt;
aw2_handlescript(&all);
free(all.glyphs);
}
free(scripts);
#if !defined(_NO_PYTHON)
FFPy_AWDataFree(&all);
#endif /* PYTHON */
}
void GuessOpticalOffset(SplineChar *sc,int layer,real *_loff, real *_roff,
int chunk_height ) {
/* Given a glyph make a guess as to where the eye thinks the left/right */
/* edges of the glyph are to be found */
/* On an "I" with no serifs, the edges are the right and left side bearings*/
/* But on an "O" the edge appears to be a bit closer to the center than the side bearing */
/* The left edge will always appear to be a non-negative (x) distance from the lsb */
/* the right edge will always appear to be a non-positve distance from the rsb */
SplineFont *sf = sc->parent;
AW_Data all;
AW_Glyph glyph, edge;
real loff, roff;
RefChar *r = HasUseMyMetrics(sc,layer);
if ( r!=NULL )
sc = r->sc;
if ( chunk_height <= 0 )
chunk_height = (sf->ascent + sf->descent)/200;
memset(&all,0,sizeof(all));
memset(&glyph,0,sizeof(glyph));
memset(&edge,0,sizeof(edge));
all.layer = layer;
all.sub_height = chunk_height;
all.sf = sf;
all.denom = (sf->ascent + sf->descent)/DENOM_FACTOR_OF_EMSIZE;
glyph.sc = sc;
SplineCharLayerFindBounds(sc,layer,&glyph.bb);
if ( glyph.bb.minx<-16000 || glyph.bb.maxx>16000 ||
glyph.bb.miny<-16000 || glyph.bb.maxy>16000 ) {
ff_post_notice(_("Glyph too big"),_("%s has a bounding box which is too big for this algorithm to work. Ignored."),sc->name);
*_loff = glyph.bb.minx;
*_roff = sc->width - glyph.bb.maxx;
} else {
aw2_findedges(&glyph, &all);
edge.imin_y = glyph.imin_y; edge.imax_y = glyph.imax_y;
aw2_dummyedges(&edge,NULL);
loff = aw2_bbox_separation(&edge,&glyph,&all);
roff = aw2_bbox_separation(&glyph,&edge,&all);
*_loff = glyph.bb.minx + loff;
*_roff = sc->width - (glyph.bb.maxx - roff);
AWGlyphFree( &glyph );
AWGlyphFree( &edge );
}
#if !defined(_NO_PYTHON)
FFPy_AWDataFree(&all);
#endif /* PYTHON */
}
void AutoKern2(SplineFont *sf, int layer,SplineChar **left,SplineChar **right,
struct lookup_subtable *into,
int separation, int min_kern, int from_closest_approach, int only_closer,
int chunk_height,
void (*addkp)(void *data,SplineChar *left,SplineChar *r,int off),
void *data) {
AW_Data all;
AW_Glyph *glyphs;
int i,cnt,k, kern;
SplineChar *sc;
KernPair *last, *kp, *next;
int is_l2r = !(into->lookup->lookup_flags & pst_r2l);
/* Normally, kerning is based on some sort of average distance between */
/* two glyphs, but sometimes it is useful to kern so much that the glyphs*/
/* just touch. If that is desired then set from_closest_approach. */
if ( chunk_height <= 0 )
chunk_height = (sf->ascent + sf->descent)/200;
if ( separation==0 && !from_closest_approach ) {
if ( into->separation==0 && !into->kerning_by_touch ) {
into->separation = sf->width_separation;
if ( sf->width_separation==0 )
into->separation = 15*(sf->ascent+sf->descent)/100;
separation = into->separation;
} else {
separation = into->separation;
from_closest_approach = into->kerning_by_touch;
min_kern = into->minkern;
only_closer = into->onlyCloser;
}
}
memset(&all,0,sizeof(all));
all.layer = layer;
all.sub_height = chunk_height;
all.desired_separation = separation;
all.sf = sf;
all.denom = (sf->ascent + sf->descent)/DENOM_FACTOR_OF_EMSIZE;
/* ticked means a left glyph, ticked2 a right (a glyph can be both) */
for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
sc->ticked = sc->ticked2 = false;
for ( i=0; (sc = left[i])!=NULL; ++i )
sc->ticked = true;
for ( i=0; (sc = right[i])!=NULL; ++i )
sc->ticked2 = true;
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
if ( sc->ticked || sc->ticked2 )
++cnt;
glyphs = calloc(cnt+1,sizeof(AW_Glyph));
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
if ( sc->ticked || sc->ticked2 ) {
SplineCharLayerFindBounds(sc,layer,&glyphs[cnt].bb);
if ( glyphs[cnt].bb.minx<-16000 || glyphs[cnt].bb.maxx>16000 ||
glyphs[cnt].bb.miny<-16000 || glyphs[cnt].bb.maxy>16000 )
ff_post_notice(_("Glyph too big"),_("%s has a bounding box which is too big for this algorithm to work. Ignored."),sc->name);
else {
glyphs[cnt].sc = sc;
aw2_findedges(&glyphs[cnt++], &all);
}
}
}
/* remove all current kern pairs in this subtable which include the specified glyphs */
if ( addkp==NULL ) {
for ( i=0; (sc = left[i])!=NULL; ++i ) {
for ( last=NULL, kp=sc->kerns; kp!=NULL; kp = next ) {
next = kp->next;
if ( kp->subtable==into && kp->sc->ticked2 ) {
if ( last==NULL )
sc->kerns = next;
else
last->next = next;
kp->next = NULL;
KernPairsFree(kp);
} else
last = kp;
}
}
}
all.glyphs = glyphs;
all.gcnt = cnt;
for ( i=0; i<cnt; ++i ) {
AW_Glyph *g1 = &glyphs[i];
if ( !g1->sc->ticked )
continue;
for ( k=0; k<cnt; ++k ) {
AW_Glyph *g2 = &glyphs[k];
if ( !g2->sc->ticked2 )
continue;
if ( from_closest_approach )
kern = rint( ak2_figure_touch(g1,g2,&all));
else {
kern = rint( ak2_figure_kern(g1,g2,&all));
if ( kern<min_kern && kern>-min_kern )
kern = 0;
}
if ( only_closer && kern>0 )
kern=0;
if ( kern!=0 ) {
if ( addkp==NULL ) {
kp = chunkalloc(sizeof(KernPair));
kp->subtable = into;
kp->off = kern;
if ( is_l2r ) {
kp->sc = g2->sc;
kp->next = g1->sc->kerns;
g1->sc->kerns = kp;
} else {
kp->sc = g1->sc;
kp->next = g2->sc->kerns;
g2->sc->kerns = kp;
}
} else
(*addkp)(data,g1->sc,g2->sc,kern);
}
}
}
for ( i=0; i<cnt; ++i )
AWGlyphFree( &glyphs[i] );
free(glyphs);
#if !defined(_NO_PYTHON)
FFPy_AWDataFree(&all);
#endif /* PYTHON */
}
void AutoKern2NewClass(SplineFont *sf,int layer,char **leftnames, char **rightnames,
int lcnt, int rcnt,
void (*kcAddOffset)(void *data,int left_index, int right_index,int offset), void *data,
int separation, int min_kern, int from_closest_approach, int only_closer,
int chunk_height) {
AW_Data all;
AW_Glyph *glyphs;
int i,cnt,k, kern;
SplineChar ***left = GlyphClassesFromNames(sf,leftnames,lcnt);
SplineChar ***right = GlyphClassesFromNames(sf,rightnames,rcnt);
int **ileft = malloc(lcnt*sizeof(int*));
int **iright = malloc(rcnt*sizeof(int*));
SplineChar **class, *sc;
if ( chunk_height <= 0 )
chunk_height = (sf->ascent + sf->descent)/200;
memset(&all,0,sizeof(all));
all.layer = layer;
all.sub_height = chunk_height;
all.desired_separation = separation;
all.sf = sf;
all.denom = (sf->ascent + sf->descent)/DENOM_FACTOR_OF_EMSIZE;
/* ticked means a left glyph, ticked2 a right (a glyph can be both) */
for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
sc->ticked = sc->ticked2 = false;
sc->ttf_glyph = -1;
}
for ( i=0; i<lcnt; ++i )
for ( class = left[i], k=0; (sc = class[k])!=NULL; ++k )
sc->ticked = true;
for ( i=0; i<rcnt; ++i )
for ( class = right[i], k=0; (sc = class[k])!=NULL; ++k )
sc->ticked2 = true;
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
if ( sc->ticked || sc->ticked2 )
++cnt;
glyphs = calloc(cnt+1,sizeof(AW_Glyph));
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
if ( sc->ticked || sc->ticked2 ) {
SplineCharLayerFindBounds(sc,layer,&glyphs[cnt].bb);
if ( glyphs[cnt].bb.minx<-16000 || glyphs[cnt].bb.maxx>16000 ||
glyphs[cnt].bb.miny<-16000 || glyphs[cnt].bb.maxy>16000 ) {
ff_post_notice(_("Glyph too big"),_("%s has a bounding box which is too big for this algorithm to work. Ignored."),sc->name);
sc->ticked = sc->ticked2 = false;
} else {
glyphs[cnt].sc = sc;
sc->ttf_glyph = cnt;
aw2_findedges(&glyphs[cnt++], &all);
}
}
}
for ( i=0; i<lcnt; ++i ) {
for ( class = left[i], k=0; (sc = class[k])!=NULL; ++k );
ileft[i] = malloc((k+1)*sizeof(int));
for ( class = left[i], k=0; (sc = class[k])!=NULL; ++k )
ileft[i][k] = sc->ttf_glyph;
ileft[i][k] = -1;
}
for ( i=0; i<rcnt; ++i ) {
for ( class = right[i], k=0; (sc = class[k])!=NULL; ++k );
iright[i] = malloc((k+1)*sizeof(int));
for ( class = right[i], k=0; (sc = class[k])!=NULL; ++k )
iright[i][k] = sc->ttf_glyph;
iright[i][k] = -1;
}
all.glyphs = glyphs;
all.gcnt = cnt;
for ( i=0; i<lcnt; ++i ) {
for ( k=0; k<rcnt; ++k ) {
if ( from_closest_approach )
kern = rint( ak2_figure_touchclass(ileft[i],iright[k],&all));
else {
kern = rint( ak2_figure_kernclass(ileft[i],iright[k],&all));
if ( kern<min_kern && kern>-min_kern )
kern = 0;
}
if ( kern>0 && only_closer )
kern = 0;
(*kcAddOffset)(data,i, k, kern);
}
}
for ( i=0; i<lcnt; ++i ) {
free(ileft[i]);
free(left[i]);
}
free(ileft); free(left);
for ( i=0; i<rcnt; ++i ) {
free(iright[i]);
free(right[i]);
}
free(iright); free(right);
free(glyphs);
}
static void kc2AddOffset(void *data,int left_index, int right_index,int offset) {
KernClass *kc = data;
if ( kc->subtable->lookup->lookup_flags & pst_r2l ) {
kc->offsets[right_index*kc->first_cnt+left_index] = offset;
} else
kc->offsets[left_index*kc->second_cnt+right_index] = offset;
}
void AutoKern2BuildClasses(SplineFont *sf,int layer,
SplineChar **leftglyphs,SplineChar **rightglyphs,
struct lookup_subtable *sub,
int separation, int min_kern, int touching, int only_closer,
int autokern,
real good_enough) {
AW_Data all;
AW_Glyph *glyphs, *me, *other;
int chunk_height;
int i,j,k,cnt,lcnt,rcnt, lclasscnt,rclasscnt;
int len;
int *lused, *rused;
char **lclassnames, **rclassnames, *str;
int *visual_separation;
KernClass *kc = sub->kc;
SplineChar *sc;
if ( kc==NULL )
return;
// 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[tmppos]); kc->firsts[tmppos] = 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[tmppos]); kc->seconds[tmppos] = 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[tmppos]); kc->firsts_names[tmppos] = 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[tmppos]); kc->seconds_names[tmppos] = 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;
if ( separation==0 && !touching ) {
/* Use default values. Generate them if they don't exist */
if ( sub->separation==0 && !sub->kerning_by_touch ) {
sub->separation = sf->width_separation;
if ( sf->width_separation==0 )
sub->separation = 15*(sf->ascent+sf->descent)/100;
separation = sub->separation;
autokern = true;
} else {
separation = sub->separation;
touching = sub->kerning_by_touch;
min_kern = sub->minkern;
only_closer = sub->onlyCloser;
autokern = !sub->dontautokern;
}
}
sub->separation = separation;
sub->minkern = min_kern;
sub->kerning_by_touch = touching;
sub->onlyCloser = only_closer;
sub->dontautokern = !autokern;
chunk_height = (sf->ascent + sf->descent)/200;
memset(&all,0,sizeof(all));
all.layer = layer;
all.sub_height = chunk_height;
all.desired_separation = separation;
all.sf = sf;
all.denom = (sf->ascent + sf->descent)/DENOM_FACTOR_OF_EMSIZE;
/* ticked means a left glyph, ticked2 a right (a glyph can be both) */
for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
sc->ticked = sc->ticked2 = false;
sc->ttf_glyph = -1;
}
for ( lcnt=0; (sc=leftglyphs[lcnt])!=NULL; ++lcnt )
sc->ticked = true;
for ( rcnt=0; (sc=rightglyphs[rcnt])!=NULL; ++rcnt )
sc->ticked2 = true;
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
if ( sc->ticked || sc->ticked2 )
++cnt;
glyphs = calloc(cnt+1,sizeof(AW_Glyph));
for ( cnt=i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
if ( sc->ticked || sc->ticked2 ) {
SplineCharLayerFindBounds(sc,layer,&glyphs[cnt].bb);
if ( glyphs[cnt].bb.minx<-16000 || glyphs[cnt].bb.maxx>16000 ||
glyphs[cnt].bb.miny<-16000 || glyphs[cnt].bb.maxy>16000 ) {
ff_post_notice(_("Glyph too big"),_("%s has a bounding box which is too big for this algorithm to work. Ignored."),sc->name);
sc->ticked = sc->ticked2 = false;
} else {
glyphs[cnt].sc = sc;
sc->ttf_glyph = cnt;
aw2_findedges(&glyphs[cnt++], &all);
}
}
}
all.glyphs = glyphs;
all.gcnt = cnt;
visual_separation = malloc(lcnt*rcnt*sizeof(int));
for ( i=0; i<lcnt; ++i ) {
int *vpt = visual_separation + i*rcnt;
SplineChar *lsc = leftglyphs[i];
if ( lsc->ticked ) {
me = &all.glyphs[lsc->ttf_glyph];
for ( j=0; j<rcnt; ++j ) {
SplineChar *rsc = rightglyphs[j];
if ( rsc->ticked2 ) {
other = &all.glyphs[rsc->ttf_glyph];
vpt[j] = aw2_bbox_separation(me,other,&all);
} else
vpt[j] = 0;
}
} else {
for ( j=0; j<rcnt; ++j )
vpt[j] = 0;
}
}
for ( i=0; i<cnt; ++i )
AWGlyphFree( &glyphs[i] );
#if !defined(_NO_PYTHON)
FFPy_AWDataFree(&all);
free(glyphs); glyphs = all.glyphs = NULL;
#endif /* PYTHON */
glyphs = all.glyphs = NULL;
good_enough *= good_enough;
lclassnames = malloc((lcnt+2) * sizeof(char *));
lclasscnt = 1;
lclassnames[0] = NULL;
lused = calloc(lcnt,sizeof(int));
for ( i=0; i<lcnt; ++i ) if ( leftglyphs[i]->ticked && !lused[i]) {
lused[i] = lclasscnt;
len = strlen(leftglyphs[i]->name)+1;
for ( j=i+1; j<lcnt; ++j ) if ( leftglyphs[j]->ticked && !lused[j] ) {
long totdiff = 0, diff;
int *vpt1 = visual_separation + i*rcnt;
int *vpt2 = visual_separation + j*rcnt;
for ( k=0; k<rcnt; ++k ) {
diff = vpt1[k] - vpt2[k];
totdiff += diff*diff;
}
if ( totdiff/((double) rcnt) < good_enough ) {
lused[j] = lclasscnt;
len += strlen(leftglyphs[j]->name)+1;
}
}
lclassnames[lclasscnt] = str = malloc(len+1);
for ( j=i; j<lcnt; ++j ) if ( lused[j]==lclasscnt ) {
strcpy(str,leftglyphs[j]->name);
str += strlen(str);
*str++ = ' ';
}
str[-1] = '\0';
++lclasscnt;
}
lclassnames[lclasscnt] = NULL;
kc = sub->kc;
kc->firsts = realloc(lclassnames,(lclasscnt+1)*sizeof(char *));
kc->first_cnt = lclasscnt;
free(lused); lused = NULL;
rclassnames = malloc((rcnt+2) * sizeof(char *));
rclasscnt = 1;
rclassnames[0] = NULL;
rused = calloc(rcnt,sizeof(int));
for ( i=0; i<rcnt; ++i ) if ( rightglyphs[i]->ticked2 && !rused[i]) {
rused[i] = rclasscnt;
len = strlen(rightglyphs[i]->name)+1;
for ( j=i+1; j<rcnt; ++j ) if ( rightglyphs[j]->ticked2 && !rused[j] ) {
long totdiff = 0, diff;
int *vpt1 = visual_separation + i;
int *vpt2 = visual_separation + j;
for ( k=0; k<lcnt; ++k ) {
diff = vpt1[k*rcnt] - vpt2[k*rcnt];
totdiff += diff*diff;
}
if ( totdiff/((double) lcnt) < good_enough ) {
rused[j] = rclasscnt;
len += strlen(rightglyphs[j]->name)+1;
}
}
rclassnames[rclasscnt] = str = malloc(len+1);
for ( j=i; j<rcnt; ++j ) if ( rused[j]==rclasscnt ) {
strcpy(str,rightglyphs[j]->name);
str += strlen(str);
*str++ = ' ';
}
str[-1] = '\0';
++rclasscnt;
}
rclassnames[rclasscnt] = NULL;
kc->seconds = realloc(rclassnames,(rclasscnt+1)*sizeof(char *));
kc->second_cnt = rclasscnt;
free(rused);
free(visual_separation);
kc->offsets = calloc(lclasscnt*rclasscnt,sizeof(int16));;
kc->adjusts = calloc(lclasscnt*rclasscnt,sizeof(DeviceTable));
if ( autokern )
AutoKern2NewClass(sf,layer,kc->firsts, kc->seconds,
kc->first_cnt, kc->second_cnt,
kc2AddOffset, kc,
separation,min_kern,touching,only_closer,chunk_height);
if ( sub->lookup->lookup_flags & pst_r2l ) {
char **temp = kc->seconds;
int tempsize = kc->second_cnt;
kc->seconds = kc->firsts;
kc->second_cnt = kc->first_cnt;
kc->firsts = temp;
kc->first_cnt = tempsize;
}
}