1 /* Copyright (C) 2005-2012 by George Williams and Alexey Kryukov */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "stemdb.h"
31 
32 #include "autohint.h"
33 #include "dumppfa.h"
34 #include "edgelist2.h"
35 #include "fontforge.h"
36 #include "splineoverlap.h"
37 #include "splineutil.h"
38 #include "splineutil2.h"
39 #include "utype.h"
40 
41 #include <math.h>
42 
43 #define GLYPH_DATA_DEBUG 0
44 
45 /* A diagonal end is like the top or bottom of a slash. Should we add a vertical stem at the end? */
46 /* A diagonal corner is like the bottom of circumflex. Should we add a horizontal stem? */
47 int     hint_diagonal_ends = 0,
48 	hint_diagonal_intersections = 0,
49 	hint_bounding_boxes = 1,
50 	detect_diagonal_stems = 0;
51 
52 float   stem_slope_error = .05061454830783555773, /*  2.9 degrees */
53 	stub_slope_error = .317649923862967983;   /* 18.2 degrees */
54 
55 static double dist_error_hv = 3.5;
56 static double dist_error_diag = 5.5;
57 /* It's easy to get horizontal/vertical lines aligned properly */
58 /* it is more difficult to get diagonal ones done */
59 /* The "A" glyph in Apple's Times.dfont(Roman) is off by 6 in one spot */
60 static double dist_error_curve = 22;
61 /* The maximum possible distance between the edge of an active zone for */
62 /* a curved spline segment and the spline itself */
63 
64 struct st {
65     Spline *s;
66     double st, lt;
67 };
68 
GetBlueFuzz(SplineFont * sf)69 static int GetBlueFuzz(SplineFont *sf) {
70     char *str, *end;
71 
72     if ( sf == NULL || sf->private == NULL ||
73 	(str=PSDictHasEntry( sf->private,"BlueFuzz" )) == NULL || !isdigit( str[0] ))
74 return 1;
75 return strtod( str, &end );
76 }
77 
IsUnitHV(BasePoint * unit,int strict)78 static int IsUnitHV( BasePoint *unit,int strict ) {
79     double angle = atan2( unit->y,unit->x );
80     double deviation = ( strict ) ? stem_slope_error : stub_slope_error;
81 
82     if ( fabs( angle ) >= FF_PI/2 - deviation && fabs( angle ) <= FF_PI/2 + deviation )
83 return( 2 );
84     else if ( fabs( angle ) <= deviation || fabs( angle ) >= FF_PI - deviation )
85 return( 1 );
86 
87 return( 0 );
88 }
89 
UnitCloserToHV(BasePoint * u1,BasePoint * u2)90 static int UnitCloserToHV( BasePoint *u1,BasePoint *u2 ) {
91     double adiff1, adiff2;
92 
93     adiff1 = fabs( atan2( u1->y,u1->x ));
94     adiff2 = fabs( atan2( u2->y,u2->x ));
95 
96     if ( adiff1 > FF_PI*.25 && adiff1 < FF_PI*.75 )
97 	adiff1 = fabs( adiff1 - FF_PI*.5 );
98     else if ( adiff1 >= FF_PI*.75 )
99 	adiff1 = FF_PI - adiff1;
100 
101     if ( adiff2 > FF_PI*.25 && adiff2 < FF_PI*.75 )
102 	adiff2 = fabs( adiff2 - FF_PI*.5 );
103     else if ( adiff2 >= FF_PI*.75 )
104 	adiff2 = FF_PI - adiff2;
105 
106     if ( adiff1 < adiff2 )
107 return( 1 );
108     else if ( adiff1 > adiff2 )
109 return( -1 );
110     else
111 return( 0 );
112 }
113 
GetUnitAngle(BasePoint * u1,BasePoint * u2)114 static double GetUnitAngle( BasePoint *u1,BasePoint *u2 ) {
115     double dx, dy;
116 
117     dy = u1->x*u2->y - u1->y*u2->x;
118     dx = u1->x*u2->x + u1->y*u2->y;
119 return( atan2( dy,dx ));
120 }
121 
UnitsOrthogonal(BasePoint * u1,BasePoint * u2,int strict)122 static int UnitsOrthogonal( BasePoint *u1,BasePoint *u2,int strict ) {
123     double angle, deviation = ( strict ) ? stem_slope_error : stub_slope_error;
124 
125     angle = GetUnitAngle( u1,u2 );
126 
127 return( fabs( angle ) >= FF_PI/2 - deviation && fabs( angle ) <= FF_PI/2 + deviation );
128 }
129 
UnitsParallel(BasePoint * u1,BasePoint * u2,int strict)130 int UnitsParallel( BasePoint *u1,BasePoint *u2,int strict ) {
131     double angle, deviation = ( strict ) ? stem_slope_error : stub_slope_error;
132 
133     angle = GetUnitAngle( u1,u2 );
134 
135 return( fabs( angle ) <= deviation || fabs( angle ) >= FF_PI - deviation );
136 }
137 
IsInflectionPoint(struct glyphdata * gd,struct pointdata * pd)138 static int IsInflectionPoint( struct glyphdata *gd,struct pointdata *pd ) {
139     SplinePoint *sp = pd->sp;
140     double CURVATURE_THRESHOLD = 1e-9;
141     struct spline *prev, *next;
142     double in, out;
143 
144     if ( sp->prev == NULL || sp->next == NULL || !pd->colinear )
145 return( false );
146 
147     /* point of a single-point contour can't be an inflection point. */
148     if ( sp->prev->from == sp )
149 return( false );
150 
151     prev = sp->prev;
152     in = 0;
153     while ( prev != NULL && fabs(in) < CURVATURE_THRESHOLD ) {
154 	in = SplineCurvature( prev,1 );
155 	if ( fabs( in ) < CURVATURE_THRESHOLD ) in = SplineCurvature( prev, 0 );
156 	if ( fabs( in ) < CURVATURE_THRESHOLD ) prev = prev->from->prev;
157 	if ( gd->points[prev->to->ptindex].colinear )
158     break;
159     }
160 
161     next = sp->next;
162     out = 0;
163     while ( next != NULL && fabs( out ) < CURVATURE_THRESHOLD ) {
164 	out = SplineCurvature( next,0 );
165 	if ( fabs( out ) < CURVATURE_THRESHOLD ) out = SplineCurvature( next, 1 );
166 	if ( fabs( out ) < CURVATURE_THRESHOLD ) next = next->to->next;
167 	if ( gd->points[next->from->ptindex].colinear )
168     break;
169     }
170 
171     if ( in==0 || out==0 || ( prev != sp->prev && next != sp->next ))
172 return( false );
173 
174     in/=fabs(in);
175     out/=fabs(out);
176 
177 return ( in*out < 0 );
178 }
179 
SplineFigureOpticalSlope(Spline * s,int start_at_from,BasePoint * dir)180 static int SplineFigureOpticalSlope(Spline *s,int start_at_from,BasePoint *dir) {
181     /* Sometimes splines have tiny control points, and to the eye the slope */
182     /*  of the spline has nothing to do with that specified by the cps. */
183     /* So see if the spline is straightish and figure the slope based on */
184     /*  some average direction */
185     /* dir is a input output parameter. */
186     /*  it should be initialized to the unit vector determined by the appropriate cp */
187     /*  if the function returns true, it will be set to a unit vector in the average direction */
188     BasePoint pos, *base, average_dir, normal;
189     double t, len, incr, off;
190     double dx, dy, ax, ay, d, a;
191 
192     /* The vector is already nearly vertical/horizontal, no need to modify*/
193     if ( IsUnitHV( dir,true ))
194 return( false );
195 
196     if ( start_at_from ) {
197 	incr = -.1;
198 	base = &s->from->me;
199     } else {
200 	incr = .1;
201 	base = &s->to->me;
202     }
203 
204     t = .5-incr;
205     memset(&average_dir,0,sizeof(average_dir));
206     while ( t>0 && t<1.0 ) {
207 	pos.x = ((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d;
208 	pos.y = ((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d;
209 
210 	average_dir.x += (pos.x-base->x); average_dir.y += (pos.y-base->y);
211 	t += incr;
212     }
213 
214     len = sqrt( pow( average_dir.x,2 ) + pow( average_dir.y,2 ));
215     if ( len==0 )
216 return( false );
217     average_dir.x /= len; average_dir.y /= len;
218     normal.x = average_dir.y; normal.y = - average_dir.x;
219 
220     t = .5-incr;
221     while ( t>0 && t<1.0 ) {
222 	pos.x = ((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d;
223 	pos.y = ((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d;
224 	off = (pos.x-base->x)*normal.x + (pos.y-base->y)*normal.y;
225 	if ( off<-dist_error_hv || off>dist_error_hv )
226 return( false );
227 	t += incr;
228     }
229 
230     if ( UnitsParallel( dir,&normal,true )) {
231 	/* prefer the direction which is closer to horizontal/vertical */
232 	if ( (dx=dir->x)<0 ) dx = -dx;
233 	if ( (dy=dir->y)<0 ) dy = -dy;
234 	d = (dx<dy) ? dx : dy;
235 	if ( (ax=average_dir.x)<0 ) ax = -ax;
236 	if ( (ay=average_dir.y)<0 ) ay = -ay;
237 	a = (ax<ay) ? ax : ay;
238 	if ( d<a )
239 return( false );
240     }
241 
242     *dir = average_dir;
243 return( true );
244 }
245 
246 static int IsSplinePeak( struct glyphdata *gd,struct pointdata *pd,int outer,int is_x,int flags );
247 
PointInit(struct glyphdata * gd,SplinePoint * sp,SplineSet * ss)248 static void PointInit( struct glyphdata *gd,SplinePoint *sp, SplineSet *ss ) {
249     struct pointdata *pd, *prevpd=NULL, *nextpd=NULL;
250     double len, same;
251     int hv;
252 
253     if ( sp->ptindex >= gd->pcnt )
254 return;
255     pd = &gd->points[sp->ptindex];
256     pd->sp = sp;
257     pd->ss = ss;
258     pd->x_extr = pd->y_extr = 0;
259     pd->base = sp->me;
260     pd->ttfindex = sp->ttfindex;
261     pd->nextcnt = pd->prevcnt = 0;
262     pd->nextstems = pd->prevstems = NULL;
263     pd->next_is_l = pd->prev_is_l = NULL;
264 
265     if ( !sp->nonextcp && gd->order2 && sp->nextcpindex < gd->realcnt ) {
266 
267 	nextpd = &gd->points[sp->nextcpindex];
268 	nextpd->ss = ss;
269 	nextpd->x_extr = nextpd->y_extr = 0;
270 	nextpd->base = sp->nextcp;
271 	nextpd->ttfindex = sp->nextcpindex;
272     }
273     if ( !sp->noprevcp && gd->order2 && sp->prev != NULL &&
274 	sp->prev->from->nextcpindex < gd->realcnt ) {
275 
276 	nextpd = &gd->points[sp->prev->from->nextcpindex];
277 	nextpd->ss = ss;
278 	nextpd->x_extr = nextpd->y_extr = 0;
279 	nextpd->base = sp->prevcp;
280 	nextpd->ttfindex = sp->prev->from->nextcpindex;
281     }
282 
283     if ( sp->next==NULL ) {
284 	pd->nextunit.x = ss->first->me.x - sp->me.x;
285 	pd->nextunit.y = ss->first->me.y - sp->me.y;
286 	pd->nextlinear = true;
287     } else if ( sp->next->knownlinear ) {
288 	pd->nextunit.x = sp->next->to->me.x - sp->me.x;
289 	pd->nextunit.y = sp->next->to->me.y - sp->me.y;
290 	pd->nextlinear = true;
291     } else if ( sp->nonextcp ) {
292 	pd->nextunit.x = sp->next->to->prevcp.x - sp->me.x;
293 	pd->nextunit.y = sp->next->to->prevcp.y - sp->me.y;
294     } else {
295 	pd->nextunit.x = sp->nextcp.x - sp->me.x;
296 	pd->nextunit.y = sp->nextcp.y - sp->me.y;
297     }
298     len = sqrt( pow( pd->nextunit.x,2 ) + pow( pd->nextunit.y,2 ));
299     if ( len==0 )
300 	pd->nextzero = true;
301     else {
302 	pd->nextlen = len;
303 	pd->nextunit.x /= len;
304 	pd->nextunit.y /= len;
305 	if ( sp->next!=NULL && !sp->next->knownlinear )
306 	    SplineFigureOpticalSlope(sp->next,true,&pd->nextunit);
307 	hv = IsUnitHV( &pd->nextunit,true );
308 	if ( hv == 2 ) {
309 	    pd->nextunit.x = 0; pd->nextunit.y = pd->nextunit.y>0 ? 1 : -1;
310 	} else if ( hv == 1 ) {
311 	    pd->nextunit.y = 0; pd->nextunit.x = pd->nextunit.x>0 ? 1 : -1;
312 	}
313 	if ( pd->nextunit.y==0 ) pd->next_hor = true;
314 	else if ( pd->nextunit.x==0 ) pd->next_ver = true;
315 
316 	if ( nextpd != NULL ) {
317 	    nextpd->prevunit.x = -pd->nextunit.x;
318 	    nextpd->prevunit.y = -pd->nextunit.y;
319 	}
320     }
321 
322     if ( sp->prev==NULL ) {
323 	pd->prevunit.x = ss->last->me.x - sp->me.x;
324 	pd->prevunit.y = ss->last->me.y - sp->me.y;
325 	pd->prevlinear = true;
326     } else if ( sp->prev->knownlinear ) {
327 	pd->prevunit.x = sp->prev->from->me.x - sp->me.x;
328 	pd->prevunit.y = sp->prev->from->me.y - sp->me.y;
329 	pd->prevlinear = true;
330     } else if ( sp->noprevcp ) {
331 	pd->prevunit.x = sp->prev->from->nextcp.x - sp->me.x;
332 	pd->prevunit.y = sp->prev->from->nextcp.y - sp->me.y;
333     } else {
334 	pd->prevunit.x = sp->prevcp.x - sp->me.x;
335 	pd->prevunit.y = sp->prevcp.y - sp->me.y;
336     }
337     len = sqrt( pow( pd->prevunit.x,2 ) + pow( pd->prevunit.y,2 ));
338     if ( len==0 )
339 	pd->prevzero = true;
340     else {
341 	pd->prevlen = len;
342 	pd->prevunit.x /= len;
343 	pd->prevunit.y /= len;
344 	if ( sp->prev!=NULL && !sp->prev->knownlinear )
345 	    SplineFigureOpticalSlope(sp->prev,false,&pd->prevunit);
346 	hv = IsUnitHV( &pd->prevunit,true );
347 	if ( hv == 2 ) {
348 	    pd->prevunit.x = 0; pd->prevunit.y = pd->prevunit.y>0 ? 1 : -1;
349 	} else if ( hv == 1 ) {
350 	    pd->prevunit.y = 0; pd->prevunit.x = pd->prevunit.x>0 ? 1 : -1;
351 	}
352 	if ( pd->prevunit.y==0 ) pd->prev_hor = true;
353 	else if ( pd->prevunit.x==0 ) pd->prev_ver = true;
354 
355 	if ( prevpd != NULL ) {
356 	    prevpd->nextunit.x = -pd->prevunit.x;
357 	    prevpd->nextunit.y = -pd->prevunit.y;
358 	}
359     }
360     {
361 	same = pd->prevunit.x*pd->nextunit.x + pd->prevunit.y*pd->nextunit.y;
362 	if ( same<-.95 )
363 	    pd->colinear = true;
364     }
365     if (( pd->prev_hor || pd->next_hor ) && pd->colinear ) {
366 	if ( IsSplinePeak( gd,pd,false,false,1 )) pd->y_extr = 1;
367 	else if ( IsSplinePeak( gd,pd,true,false,1 )) pd->y_extr = 2;
368     } else if (( pd->prev_ver || pd->next_ver ) && pd->colinear ) {
369 	if ( IsSplinePeak( gd,pd,true,true,1 )) pd->x_extr = 1;
370 	else if ( IsSplinePeak( gd,pd,false,true,1 )) pd->x_extr = 2;
371     } else {
372 	if (( pd->nextunit.y < 0 && pd->prevunit.y < 0 ) || ( pd->nextunit.y > 0 && pd->prevunit.y > 0 )) {
373 	    if ( IsSplinePeak( gd,pd,false,false,2 )) pd->y_corner = 1;
374 	    else if ( IsSplinePeak( gd,pd,true,false,2 )) pd->y_corner = 2;
375 	}
376 	if (( pd->nextunit.x < 0 && pd->prevunit.x < 0 ) || ( pd->nextunit.x > 0 && pd->prevunit.x > 0 )) {
377 	    if ( IsSplinePeak( gd,pd,true,true,2 )) pd->x_corner = 1;
378 	    else if ( IsSplinePeak( gd,pd,false,true,2 )) pd->x_corner = 2;
379 	}
380     }
381     if ( hint_diagonal_intersections ) {
382 	if (( pd->y_corner || pd->y_extr ) &&
383 	    RealNear( pd->nextunit.x,-pd->prevunit.x ) &&
384 	    RealNear( pd->nextunit.y,pd->prevunit.y ) && !pd->nextzero)
385 	    pd->symetrical_h = true;
386 	else if (( pd->x_corner || pd->x_extr ) &&
387 	    RealNear( pd->nextunit.y,-pd->prevunit.y ) &&
388 	    RealNear( pd->nextunit.x,pd->prevunit.x ) && !pd->nextzero)
389 	    pd->symetrical_v = true;
390     }
391 }
392 
BBoxIntersectsLine(Spline * s,Spline * line)393 static int BBoxIntersectsLine(Spline *s,Spline *line) {
394     double t,x,y;
395     DBounds b;
396 
397     b.minx = b.maxx = s->from->me.x;
398     b.miny = b.maxy = s->from->me.y;
399     if ( s->to->me.x<b.minx ) b.minx = s->to->me.x;
400     else if ( s->to->me.x>b.maxx ) b.maxx = s->to->me.x;
401     if ( s->to->me.y<b.miny ) b.miny = s->to->me.y;
402     else if ( s->to->me.y>b.maxy ) b.maxy = s->to->me.y;
403     if ( s->to->prevcp.x<b.minx ) b.minx = s->to->prevcp.x;
404     else if ( s->to->prevcp.x>b.maxx ) b.maxx = s->to->prevcp.x;
405     if ( s->to->prevcp.y<b.miny ) b.miny = s->to->prevcp.y;
406     else if ( s->to->prevcp.y>b.maxy ) b.maxy = s->to->prevcp.y;
407     if ( s->from->nextcp.x<b.minx ) b.minx = s->from->nextcp.x;
408     else if ( s->from->nextcp.x>b.maxx ) b.maxx = s->from->nextcp.x;
409     if ( s->from->nextcp.y<b.miny ) b.miny = s->from->nextcp.y;
410     else if ( s->from->nextcp.y>b.maxy ) b.maxy = s->from->nextcp.y;
411 
412     if ( line->splines[0].c!=0 ) {
413 	t = (b.minx-line->splines[0].d)/line->splines[0].c;
414 	y = line->splines[1].c*t+line->splines[1].d;
415 	if ( y>=b.miny && y<=b.maxy )
416 return( true );
417 	t = (b.maxx-line->splines[0].d)/line->splines[0].c;
418 	y = line->splines[1].c*t+line->splines[1].d;
419 	if ( y>=b.miny && y<=b.maxy )
420 return( true );
421     }
422     if ( line->splines[1].c!=0 ) {
423 	t = (b.miny-line->splines[1].d)/line->splines[1].c;
424 	x = line->splines[0].c*t+line->splines[0].d;
425 	if ( x>=b.minx && x<=b.maxx )
426 return( true );
427 	t = (b.maxy-line->splines[1].d)/line->splines[1].c;
428 	x = line->splines[0].c*t+line->splines[0].d;
429 	if ( x>=b.minx && x<=b.maxx )
430 return( true );
431     }
432 return( false );
433 }
434 
stcmp(const void * _p1,const void * _p2)435 static int stcmp(const void *_p1, const void *_p2) {
436     const struct st *stpt1 = _p1, *stpt2 = _p2;
437     if ( stpt1->lt>stpt2->lt )
438 return( 1 );
439     else if ( stpt1->lt<stpt2->lt )
440 return( -1 );
441 
442 return( 0 );
443 }
444 
line_pt_cmp(const void * _p1,const void * _p2)445 static int line_pt_cmp( const void *_p1, const void *_p2 ) {
446     struct pointdata * const *pd1 = _p1, * const *pd2 = _p2;
447     struct linedata *line;
448     double ppos1=0,ppos2=0;
449 
450     if ( (*pd1)->prevline != NULL &&
451 	( (*pd1)->prevline == (*pd2)->prevline || (*pd1)->prevline == (*pd2)->nextline ))
452 	line = (*pd1)->prevline;
453     else if ( (*pd1)->nextline != NULL &&
454 	( (*pd1)->nextline == (*pd2)->prevline || (*pd1)->nextline == (*pd2)->nextline ))
455 	line = (*pd1)->nextline;
456     else
457 return( 0 );
458 
459     ppos1 = ( (*pd1)->sp->me.x - line->online.x ) * line->unit.x +
460 	    ( (*pd1)->sp->me.y - line->online.y ) * line->unit.y;
461     ppos2 = ( (*pd2)->sp->me.x - line->online.x ) * line->unit.x +
462 	    ( (*pd2)->sp->me.y - line->online.y ) * line->unit.y;
463 
464     if ( ppos1>ppos2 )
465 return( 1 );
466     else if ( ppos1<ppos2 )
467 return( -1 );
468     else
469 return( 0 );
470 }
471 
segment_cmp(const void * _s1,const void * _s2)472 static int segment_cmp(const void *_s1, const void *_s2) {
473     const struct segment *s1 = _s1, *s2 = _s2;
474     if ( s1->start<s2->start )
475 return( -1 );
476     else if ( s1->start>s2->start )
477 return( 1 );
478 
479 return( 0 );
480 }
481 
proj_cmp(const void * _p1,const void * _p2)482 static int proj_cmp(const void *_p1, const void *_p2) {
483     struct pointdata * const *p1 = _p1, * const *p2 = _p2;
484     if ( (*p1)->projection<(*p2)->projection )
485 return( -1 );
486     else if ( (*p1)->projection>(*p2)->projection )
487 return( 1 );
488 
489 return( 0 );
490 }
491 
AssignStemToPoint(struct pointdata * pd,struct stemdata * stem,int is_next,int left)492 static void AssignStemToPoint( struct pointdata *pd,struct stemdata *stem,int is_next, int left ) {
493     struct stemdata ***stems;
494     int i, *stemcnt, **is_l;
495 
496     stems = ( is_next ) ? &pd->nextstems : &pd->prevstems;
497     stemcnt = ( is_next ) ? &pd->nextcnt : &pd->prevcnt;
498     is_l = ( is_next ) ? &pd->next_is_l : &pd->prev_is_l;
499     for ( i=0; i<*stemcnt; i++ ) {
500 	if ((*stems)[i] == stem )
501 return;
502     }
503 
504     *stems = realloc( *stems,( *stemcnt+1 )*sizeof( struct stemdata *));
505     *is_l  = realloc( *is_l, ( *stemcnt+1 )*sizeof( int ));
506     (*stems)[*stemcnt] = stem;
507     (*is_l )[*stemcnt] = left;
508     (*stemcnt)++;
509 }
510 
IsStemAssignedToPoint(struct pointdata * pd,struct stemdata * stem,int is_next)511 int IsStemAssignedToPoint( struct pointdata *pd,struct stemdata *stem,int is_next ) {
512     struct stemdata **stems;
513     int i, stemcnt;
514 
515     stems = ( is_next ) ? pd->nextstems : pd->prevstems;
516     stemcnt = ( is_next ) ? pd->nextcnt : pd->prevcnt;
517 
518     for ( i=0; i<stemcnt; i++ ) {
519 	if ( stems[i] == stem )
520 return( i );
521     }
522 return( -1 );
523 }
524 
GetValidPointDataIndex(struct glyphdata * gd,SplinePoint * sp,struct stemdata * stem)525 static int GetValidPointDataIndex( struct glyphdata *gd,SplinePoint *sp,
526     struct stemdata *stem ) {
527 
528     struct pointdata *tpd;
529 
530     if ( sp == NULL )
531 return( -1 );
532     if ( sp->ttfindex < gd->realcnt )
533 return( sp->ttfindex );
534     if ( !sp->nonextcp && sp->nextcpindex < gd->realcnt ) {
535 	tpd = &gd->points[sp->nextcpindex];
536 	if ( IsStemAssignedToPoint( tpd,stem,false ) != -1 )
537 return( sp->nextcpindex );
538     }
539     if ( !sp->noprevcp && sp->prev != NULL &&
540 	sp->prev->from->nextcpindex < gd->realcnt ) {
541 	tpd = &gd->points[sp->prev->from->nextcpindex];
542 	if ( IsStemAssignedToPoint( tpd,stem,true ) != -1 )
543 return( sp->prev->from->nextcpindex );
544     }
545 return( -1 );
546 }
547 
LineType(struct st * st,int i,int cnt,Spline * line)548 static int LineType(struct st *st,int i, int cnt,Spline *line) {
549     SplinePoint *sp;
550     BasePoint nextcp, prevcp, here;
551     double dn, dp;
552 
553     if ( st[i].st>.01 && st[i].st<.99 )
554 return( 0 );		/* Not near an end-point, just a normal line */
555     if ( i+1>=cnt )
556 return( 0 );		/* No following spline */
557     if ( st[i+1].st>.01 && st[i+1].st<.99 )
558 return( 0 );		/* Following spline not near an end-point, can't */
559 			/*  match to this one, just a normal line */
560     if ( st[i].st<.5 && st[i+1].st>.5 ) {
561 	if ( st[i+1].s->to->next!=st[i].s )
562 return( 0 );
563 	sp = st[i].s->from;
564     } else if ( st[i].st>.5 && st[i+1].st<.5 ) {
565 	if ( st[i].s->to->next!=st[i+1].s )
566 return( 0 );
567 	sp = st[i].s->to;
568     } else
569 return( 0 );
570 
571     if ( !sp->nonextcp )
572 	nextcp = sp->nextcp;
573     else
574 	nextcp = sp->next->to->me;
575     if ( !sp->noprevcp )
576 	prevcp = sp->prevcp;
577     else
578 	prevcp = sp->prev->from->me;
579     here.x = line->splines[0].c*(st[i].st+st[i+1].st)/2 + line->splines[0].d;
580     here.y = line->splines[1].c*(st[i].st+st[i+1].st)/2 + line->splines[1].d;
581 
582     nextcp.x -= here.x; nextcp.y -= here.y;
583     prevcp.x -= here.x; prevcp.y -= here.y;
584 
585     dn = nextcp.x*line->splines[1].c - nextcp.y*line->splines[0].c;
586     dp = prevcp.x*line->splines[1].c - prevcp.y*line->splines[0].c;
587     if ( dn*dp<0 )	/* splines away move on opposite sides of the line */
588 return( 1 );		/* Treat this line and the next as one */
589 			/* We assume that a rounding error gave us one erroneous intersection (or we went directly through the endpoint) */
590     else
591 return( 2 );		/* Ignore both this line and the next */
592 			/* Intersects both in a normal fashion */
593 }
594 
MonotonicOrder(Spline ** sspace,Spline * line,struct st * stspace)595 static int MonotonicOrder(Spline **sspace,Spline *line,struct st *stspace) {
596     Spline *s;
597     int i,j,k,cnt;
598     BasePoint pts[9];
599     extended lts[10], sts[10];
600 
601     for ( i=j=0; (s=sspace[j])!=NULL; ++j ) {
602 	if ( BBoxIntersectsLine(s,line) ) {
603 	    /* Lines parallel to the direction we are testing just get in the */
604 	    /*  way and don't add any useful info */
605 	    if ( s->islinear &&
606 		    RealNear(line->splines[0].c*s->splines[1].c,
607 			    line->splines[1].c*s->splines[0].c))
608     continue;
609 	    if ( SplinesIntersect(line,s,pts,lts,sts)<=0 )
610     continue;
611 	    for ( k=0; sts[k]!=-1; ++k ) {
612 		if ( sts[k]>=0 && sts[k]<=1 ) {
613 		    stspace[i].s    = s;
614 		    stspace[i].lt   = lts[k];
615 		    stspace[i++].st = sts[k];
616 		}
617 	    }
618 	}
619     }
620     stspace[i].s = NULL;
621     cnt = i;
622     qsort(stspace,cnt,sizeof(struct st),stcmp);
623 return( cnt );
624 }
625 
MonotonicFindAlong(Spline * line,struct st * stspace,int cnt,Spline * findme,double * other_t)626 static Spline *MonotonicFindAlong(Spline *line,struct st *stspace,int cnt,
627 	Spline *findme, double *other_t) {
628     Spline *s;
629     int i;
630     int eo;		/* I do horizontal/vertical by winding number */
631 			/* But figuring winding number with respect to a */
632 			/* diagonal line is hard. So I use even-odd */
633 			/* instead. */
634 
635     eo = 0;
636     for ( i=0; i<cnt; ++i ) {
637 	s = stspace[i].s;
638 	if ( s==findme ) {
639 	    if ( (eo&1) && i>0 ) {
640 		*other_t = stspace[i-1].st;
641 return( stspace[i-1].s );
642 	    } else if ( !(eo&1) && i+1<cnt ) {
643 		*other_t = stspace[i+1].st;
644 return( stspace[i+1].s );
645 	    }
646 	    fprintf( stderr, "MonotonicFindAlong: Ran out of intersections.\n" );
647 return( NULL );
648 	}
649 	if ( i+1<cnt && stspace[i+1].s==findme )
650 	    ++eo;
651 	else switch ( LineType(stspace,i,cnt,line) ) {
652 	  case 0:	/* Normal spline */
653 	    ++eo;
654 	  break;
655 	  case 1:	/* Intersects at end-point & next entry is other side */
656 	    ++eo;	/*  And the two sides continue in approximately the   */
657 	    ++i;	/*  same direction */
658 	  break;
659 	  case 2:	/* Intersects at end-point & next entry is other side */
660 	    ++i;	/*  And the two sides go in opposite directions */
661 	  break;
662 	  default:
663 	  break;
664 }
665     }
666     fprintf( stderr, "MonotonicFindAlong: Never found our spline.\n" );
667 return( NULL );
668 }
669 
MonotonicFindStemBounds(Spline * line,struct st * stspace,int cnt,double fudge,struct stemdata * stem)670 static int MonotonicFindStemBounds(Spline *line,struct st *stspace,int cnt,
671 	double fudge,struct stemdata *stem ) {
672     int i,j;
673     int eo;		/* I do horizontal/vertical by winding number */
674 			/* But figuring winding number with respect to a */
675 			/* diagonal line is hard. So I use even-odd */
676 			/* instead. */
677     double pos, npos;
678     double lmin = ( stem->lmin < -fudge ) ? stem->lmin : -fudge;
679     double lmax = ( stem->lmax > fudge ) ? stem->lmax : fudge;
680     double rmin = ( stem->rmin < -fudge ) ? stem->rmin : -fudge;
681     double rmax = ( stem->rmax > fudge ) ? stem->rmax : fudge;
682     lmin -= .0001; lmax += .0001; rmin -= .0001; rmax += .0001;
683 
684     eo = 0;
685     for ( i=0; i<cnt; ++i ) {
686 	pos =   (line->splines[0].c*stspace[i].lt + line->splines[0].d - stem->left.x)*stem->l_to_r.x +
687 		(line->splines[1].c*stspace[i].lt + line->splines[1].d - stem->left.y)*stem->l_to_r.y;
688 	npos = 1e4;
689 	if ( i+1<cnt )
690 	    npos = (line->splines[0].c*stspace[i+1].lt + line->splines[0].d - stem->left.x)*stem->l_to_r.x +
691 		   (line->splines[1].c*stspace[i+1].lt + line->splines[1].d - stem->left.y)*stem->l_to_r.y;
692 
693 	if ( pos>=lmin && pos<=lmax ) {
694 	    if ( (eo&1) && i>0 )
695 		j = i-1;
696 	    else if ( !(eo&1) && i+1<cnt )
697 		j = i+1;
698 	    else
699 return( false );
700 	    pos = (line->splines[0].c*stspace[j].lt + line->splines[0].d - stem->right.x)*stem->l_to_r.x +
701 		  (line->splines[1].c*stspace[j].lt + line->splines[1].d - stem->right.y)*stem->l_to_r.y;
702 	    if ( pos >= rmin && pos <= rmax )
703 return( true );
704 	}
705 	if ( i+1 < cnt && npos >= lmin && npos <= lmax )
706 	    ++eo;
707 	else switch ( LineType(stspace,i,cnt,line) ) {
708 	  case 0:	/* Normal spline */
709 	    ++eo;
710 	  break;
711 	  case 1:	/* Intersects at end-point & next entry is other side */
712 	    ++eo;	/*  And the two sides continue in approximately the   */
713 	    ++i;	/*  same direction */
714 	  break;
715 	  case 2:	/* Intersects at end-point & next entry is other side */
716 	    ++i;	/*  And the two sides go in opposite directions */
717 	  break;
718 	  default:
719 	  break;
720 	}
721     }
722 return( false );
723 }
724 
MatchWinding(struct monotonic ** space,int i,int nw,int winding,int which,int idx)725 static int MatchWinding(struct monotonic ** space,int i,int nw,int winding,int which,int idx) {
726     struct monotonic *m;
727     int j,cnt=0;
728 
729     if (( nw<0 && winding>0 ) || (nw>0 && winding<0)) {
730 	winding = nw;
731 	for ( j=i-1; j>=0; --j ) {
732 	    m = space[j];
733 	    winding += ((&m->xup)[which] ? 1 : -1 );
734 	    if ( winding==0 ) {
735 		if ( cnt == idx )
736 return( j );
737 		cnt++;
738 	    }
739 	}
740     } else {
741 	winding = nw;
742 	for ( j=i+1; space[j]!=NULL; ++j ) {
743 	    m = space[j];
744 	    winding += ((&m->xup)[which] ? 1 : -1 );
745 	    if ( winding==0 ) {
746 		if ( cnt == idx )
747 return( j );
748 		cnt++;
749 	    }
750 	}
751     }
752 return( -1 );
753 }
754 
FindMatchingHVEdge(struct glyphdata * gd,struct pointdata * pd,int is_next,Spline ** edges,double * other_t,double * dist)755 static int FindMatchingHVEdge( struct glyphdata *gd,struct pointdata *pd,
756     int is_next,Spline **edges,double *other_t,double *dist ) {
757 
758     double test, t, start, end;
759     int which;
760     Spline *s;
761     Monotonic *m;
762     int winding, nw, i, j, ret=0;
763     struct monotonic **space;
764     BasePoint *dir, d, hv;
765 
766     /* Things are difficult if we go exactly through the point. Move off */
767     /*  to the side a tiny bit and hope that doesn't matter */
768     if ( is_next==2 ) {
769 	/* Consider the case of the bottom of the circumflex (or a chevron) */
770 	/*  Think of it as a flattend breve. It is symetrical and we want to */
771 	/*  note the vertical distance between the two points that define */
772 	/*  the bottom, so treat them as a funky stem */
773 	/*                 \ \     / /              */
774 	/*                  \ \   / /               */
775 	/*                   \ \ / /                */
776 	/*                    \ + /                 */
777 	/*                     \ /                  */
778 	/*                      +                   */
779 	hv.x = pd->symetrical_h ? 1.0 : 0.0;
780 	hv.y = pd->symetrical_v ? 1.0 : 0.0;
781 	dir = &hv;
782 	t = .001;
783 	s = pd->sp->next;		/* Could just as easily be prev */
784     } else if ( is_next ) {
785 	s = pd->sp->next;
786 	t = .001;
787 	dir = &pd->nextunit;
788     } else {
789 	s = pd->sp->prev;
790 	t = .999;
791 	dir = &pd->prevunit;
792     }
793     if (( d.x = dir->x )<0 ) d.x = -d.x;
794     if (( d.y = dir->y )<0 ) d.y = -d.y;
795     which = d.x<d.y;		/* closer to vertical */
796 
797     if ( s==NULL )		/* Somehow we got an open contour? */
798 return( 0 );
799 
800     test = ((s->splines[which].a*t+s->splines[which].b)*t+s->splines[which].c)*t+s->splines[which].d;
801     MonotonicFindAt(gd->ms,which,test,space = gd->space);
802 
803     winding = 0;
804     for ( i=0; space[i]!=NULL; ++i ) {
805 	m = space[i];
806 	nw = ((&m->xup)[which] ? 1 : -1 );
807 	if ( m->s == s && t>=m->tstart && t<=m->tend ) {
808 	    start = m->other;
809     break;
810 	}
811 	winding += nw;
812     }
813     if ( space[i]==NULL ) {
814 	fprintf( stderr, "FindMatchinHVEdge didn't\n" );
815 return( 0 );
816     }
817 
818     j = MatchWinding(space,i,nw,winding,which,0);
819     if ( j!=-1 ) {
820 	other_t[0] = space[j]->t;
821 	end = space[j]->other;
822 	dist[0] = end - start;
823 	if ( dist[0] < 0 ) dist[0] = -dist[0];
824 	edges[0] = space[j]->s;
825 	ret++;
826     }
827     if ( ret > 0 && is_next != 2 && ( pd->x_extr == 1 || pd->y_extr == 1 )) {
828 	j = MatchWinding(space,i,nw,winding,which,1);
829 	if ( j!=-1 ) {
830 	    other_t[ret] = space[j]->t;
831 	    end = space[j]->other;
832 	    dist[ret] = end - start;
833 	    if ( dist[ret] < 0 ) dist[ret] = -dist[ret];
834 	    edges[ret] = space[j]->s;
835 	    ret++;
836 	}
837     }
838 return( ret );
839 }
840 
PerturbAlongSpline(Spline * s,BasePoint * bp,double t)841 static BasePoint PerturbAlongSpline( Spline *s,BasePoint *bp,double t ) {
842     BasePoint perturbed;
843 
844     for (;;) {
845 	perturbed.x = ((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d;
846 	perturbed.y = ((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d;
847 	if ( !RealWithin( perturbed.x,bp->x,.01 ) || !RealWithin( perturbed.y,bp->y,.01 ))
848     break;
849 	if ( t<.5 ) {
850 	    t *= 2;
851 	    if ( t>.5 )
852     break;
853 	} else {
854 	    t = 1- 2*(1-t);
855 	    if ( t<.5 )
856     break;
857 	}
858     }
859 return( perturbed );
860 }
861 
MakeVirtualLine(struct glyphdata * gd,BasePoint * perturbed,BasePoint * dir,Spline * myline,SplinePoint * end1,SplinePoint * end2)862 static void MakeVirtualLine(struct glyphdata *gd,BasePoint *perturbed,
863     BasePoint *dir,Spline *myline,SplinePoint *end1, SplinePoint *end2) {
864 
865     BasePoint norm, absnorm;
866     SplineSet *spl;
867     Spline *s, *first;
868     double t1, t2;
869     int i, cnt;
870 
871     if ( gd->stspace==NULL ) {
872 	for ( i=0; i<2; ++i ) {
873 	    cnt = 0;
874 	    for ( spl=gd->sc->layers[gd->layer].splines; spl!=NULL; spl=spl->next ) {
875 		first = NULL;
876 		if ( spl->first->prev!=NULL ) {
877 		    for ( s=spl->first->next; s!=first; s=s->to->next ) {
878 			if ( first==NULL ) first = s;
879 			if ( i )
880 			    gd->sspace[cnt] = s;
881 			++cnt;
882 		    }
883 		}
884 	    }
885 	    if ( !i ) {
886 		gd->scnt = cnt;
887 		gd->sspace = malloc((cnt+1)*sizeof(Spline *));
888 	    } else
889 		gd->sspace[cnt] = NULL;
890 	}
891 	gd->stspace = malloc((3*cnt+2)*sizeof(struct st));
892 	SplineCharFindBounds(gd->sc,&gd->size);
893 	gd->size.minx -= 10; gd->size.miny -= 10;
894 	gd->size.maxx += 10; gd->size.maxy += 10;
895     }
896 
897     norm.x = -dir->y;
898     norm.y = dir->x;
899     absnorm = norm;
900     if ( absnorm.x<0 ) absnorm.x = -absnorm.x;
901     if ( absnorm.y<0 ) absnorm.y = -absnorm.y;
902 
903     memset(myline,0,sizeof(*myline));
904     memset(end1,0,sizeof(*end1));
905     memset(end2,0,sizeof(*end2));
906     myline->knownlinear = myline->islinear = true;
907 
908     if ( absnorm.x > absnorm.y ) {
909 	/* Greater change in x than in y */
910 	t1 = (gd->size.minx-perturbed->x)/norm.x;
911 	t2 = (gd->size.maxx-perturbed->x)/norm.x;
912 	myline->splines[0].d = gd->size.minx;
913 	myline->splines[0].c = gd->size.maxx-gd->size.minx;
914 	myline->splines[1].d = perturbed->y+t1*norm.y;
915 	myline->splines[1].c = (t2-t1)*norm.y;
916     } else {
917 	t1 = (gd->size.miny-perturbed->y)/norm.y;
918 	t2 = (gd->size.maxy-perturbed->y)/norm.y;
919 	myline->splines[1].d = gd->size.miny;
920 	myline->splines[1].c = gd->size.maxy-gd->size.miny;
921 	myline->splines[0].d = perturbed->x+t1*norm.x;
922 	myline->splines[0].c = (t2-t1)*norm.x;
923     }
924     end1->me.x = myline->splines[0].d;
925     end2->me.x = myline->splines[0].d + myline->splines[0].c;
926     end1->me.y = myline->splines[1].d;
927     end2->me.y = myline->splines[1].d + myline->splines[1].c;
928     end1->nextcp = end1->prevcp = end1->me;
929     end2->nextcp = end2->prevcp = end2->me;
930     end1->nonextcp = end1->noprevcp = end2->nonextcp = end2->noprevcp = true;
931     end1->next = myline; end2->prev = myline;
932     myline->from = end1; myline->to = end2;
933 }
934 
FindMatchingEdge(struct glyphdata * gd,struct pointdata * pd,int is_next,Spline ** edges)935 static int FindMatchingEdge( struct glyphdata *gd, struct pointdata *pd,
936     int is_next,Spline **edges ) {
937 
938     BasePoint *dir, vert, perturbed, diff;
939     Spline myline;
940     SplinePoint end1, end2;
941     double *other_t = is_next==2 ? &pd->both_e_t : is_next ? pd->next_e_t : pd->prev_e_t;
942     double *dist = is_next ? pd->next_dist : pd->prev_dist;
943     double t ;
944     Spline *s;
945     int cnt;
946 
947     dist[0] = 0; dist[1] = 0;
948     if (( is_next && ( pd->next_hor || pd->next_ver )) ||
949 	( !is_next && ( pd->prev_hor || pd->prev_ver )) ||
950 	is_next == 2 )
951 return( FindMatchingHVEdge(gd,pd,is_next,edges,other_t,dist));
952 
953     if ( is_next ) {
954 	dir = &pd->nextunit;
955 	t = .001;
956 	s = pd->sp->next;
957     } else {
958 	dir = &pd->prevunit;
959 	t = .999;
960 	s = pd->sp->prev;
961     }
962     /* For spline segments which have slope close enough to the font's italic */
963     /* slant look for an opposite edge along the horizontal direction, rather */
964     /* than along the normal for the point's next/previous unit. This allows  */
965     /* us e. g. to detect serifs in italic fonts */
966     if ( gd->has_slant ) {
967 	if ( UnitsParallel( dir,&gd->slant_unit,true )) {
968 	    vert.x = 0; vert.y = 1;
969 	    dir = &vert;
970 	}
971     }
972 
973     if ( s==NULL || ( gd->only_hv && !IsUnitHV( dir,false )))
974 return( 0 );
975 
976     diff.x = s->to->me.x-s->from->me.x; diff.y = s->to->me.y-s->from->me.y;
977     if ( diff.x<.03 && diff.x>-.03 && diff.y<.03 && diff.y>-.03 )
978 return( 0 );
979 
980     /* Don't base the line on the current point, we run into rounding errors */
981     /*  where lines that should intersect it don't. Instead perturb it a tiny*/
982     /*  bit in the direction along the spline */
983     perturbed = PerturbAlongSpline( s,&pd->sp->me,t );
984 
985     MakeVirtualLine(gd,&perturbed,dir,&myline,&end1,&end2);
986     /* prev_e_t = next_e_t = both_e_t =. This is where these guys are set */
987     cnt = MonotonicOrder(gd->sspace,&myline,gd->stspace);
988     edges[0] = MonotonicFindAlong(&myline,gd->stspace,cnt,s,other_t);
989 return( edges[0] != NULL );
990 }
991 
StillStem(struct glyphdata * gd,double fudge,BasePoint * pos,struct stemdata * stem)992 static int StillStem(struct glyphdata *gd,double fudge,BasePoint *pos,struct stemdata *stem ) {
993     Spline myline;
994     SplinePoint end1, end2;
995     int cnt, ret;
996 
997     MakeVirtualLine( gd,pos,&stem->unit,&myline,&end1,&end2 );
998     cnt = MonotonicOrder( gd->sspace,&myline,gd->stspace );
999     ret = MonotonicFindStemBounds( &myline,gd->stspace,cnt,fudge,stem );
1000 return( ret );
1001 }
1002 
CornerCorrectSide(struct pointdata * pd,int x_dir,int is_l)1003 static int CornerCorrectSide( struct pointdata *pd,int x_dir,int is_l ) {
1004     int corner = ( x_dir ) ? pd->x_corner : pd->y_corner;
1005     int start = (( x_dir && is_l ) || ( !x_dir && !is_l ));
1006     double unit_p, unit_n;
1007 
1008     unit_p = (&pd->prevunit.x)[!x_dir];
1009     unit_n = (&pd->nextunit.x)[!x_dir];
1010 return( ( start && (
1011 	( corner == 1 && unit_p > 0 && unit_n > 0 ) ||
1012 	( corner == 2 && unit_p < 0 && unit_n < 0 ))) ||
1013 	( !start && (
1014 	( corner == 1 && unit_p < 0 && unit_n < 0 ) ||
1015 	( corner == 2 && unit_p > 0 && unit_n > 0 ))));
1016 }
1017 
IsCorrectSide(struct glyphdata * gd,struct pointdata * pd,int is_next,int is_l,BasePoint * dir)1018 static int IsCorrectSide( struct glyphdata *gd,struct pointdata *pd,
1019     int is_next,int is_l,BasePoint *dir ) {
1020 
1021     Spline *sbase, myline;
1022     SplinePoint *sp = pd->sp, end1, end2;
1023     BasePoint perturbed;
1024     int i, hv, is_x, ret = false, winding = 0, cnt, eo;
1025     double t, test;
1026     struct monotonic **space, *m;
1027 
1028     hv = IsUnitHV( dir,true );
1029     if (( hv == 2 && pd->x_corner ) || ( hv == 1 && pd->y_corner ))
1030 return( CornerCorrectSide( pd,( hv == 2 ),is_l ));
1031 
1032     sbase = ( is_next ) ? sp->next : sp->prev;
1033     t = ( is_next ) ? 0.001 : 0.999;
1034     perturbed = PerturbAlongSpline( sbase,&sp->me,t );
1035 
1036     if ( hv ) {
1037 	is_x = ( hv == 2 );
1038 	test = ( is_x ) ? perturbed.y : perturbed.x;
1039 	MonotonicFindAt( gd->ms,is_x,test,space = gd->space );
1040 	for ( i=0; space[i]!=NULL; ++i ) {
1041 	    m = space[i];
1042 	    winding = ((&m->xup)[is_x] ? 1 : -1 );
1043 	    if ( m->s == sbase )
1044 	break;
1045 	}
1046 	if ( space[i]!=NULL )
1047 	    ret = (( is_l && winding == 1 ) || ( !is_l && winding == -1 ));
1048     } else {
1049 	MakeVirtualLine( gd,&perturbed,dir,&myline,&end1,&end2 );
1050 	cnt = MonotonicOrder( gd->sspace,&myline,gd->stspace );
1051 	eo = -1;
1052 	is_x = fabs( dir->y ) > fabs( dir->x );
1053 	/* If a diagonal stem is more vertical than horizontal, then our     */
1054 	/* virtual line will go from left to right. It will first intersect  */
1055 	/* the left side of the stem, if the stem also points north-east.    */
1056 	/* In any other case the virtual line will first intersect the right */
1057 	/* side. */
1058 	i = ( is_x && dir->y > 0 ) ? 0 : cnt-1;
1059 	while ( i >= 0 && i <= cnt-1 ) {
1060 	    eo = ( eo != 1 ) ? 1 : 0;
1061 	    if ( gd->stspace[i].s == sbase )
1062 	break;
1063 	    if ( is_x && dir->y > 0 ) i++;
1064 	    else i--;
1065 	}
1066 	ret = ( is_l == eo );
1067     }
1068 return( ret );
1069 }
1070 
1071 /* In TrueType I want to make sure that everything on a diagonal line remains */
1072 /*  on the same line. Hence we compute the line. Also we are interested in */
1073 /*  points that are on the intersection of two lines */
BuildLine(struct glyphdata * gd,struct pointdata * pd,int is_next)1074 static struct linedata *BuildLine(struct glyphdata *gd,struct pointdata *pd,int is_next ) {
1075     int i;
1076     BasePoint *dir, *base, *start, *end;
1077     struct pointdata **pspace = gd->pspace, *pd2;
1078     int pcnt=0, is_l, hv;
1079     double dist_error;
1080     struct linedata *line;
1081     double off, firstoff, lastoff, lmin=0, lmax=0;
1082 
1083     dir = is_next ? &pd->nextunit : &pd->prevunit;
1084     is_l = IsCorrectSide( gd,pd,is_next,true,dir );
1085     dist_error = ( IsUnitHV( dir,true )) ? dist_error_hv : dist_error_diag ;	/* Diagonals are harder to align */
1086     if ( dir->x==0 && dir->y==0 )
1087 return( NULL );
1088     base = &pd->sp->me;
1089 
1090     for ( i= (pd - gd->points)+1; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
1091 	pd2 = &gd->points[i];
1092 	off =  ( pd2->sp->me.x - base->x )*dir->y -
1093 	       ( pd2->sp->me.y - base->y )*dir->x;
1094 	if ( off <= lmax - 2*dist_error || off >= lmin + 2*dist_error )
1095     continue;
1096 	if ( off < 0 && off < lmin ) lmin = off;
1097 	else if ( off > 0 && off > lmax ) lmax = off;
1098 
1099 	if ((( UnitsParallel( dir,&pd2->nextunit,true ) && pd2->nextline==NULL ) &&
1100 	    IsCorrectSide( gd,pd2,true,is_l,dir )) ||
1101 	    (( UnitsParallel( dir,&pd2->prevunit,true ) && pd2->prevline==NULL ) &&
1102 	    IsCorrectSide( gd,pd2,false,is_l,dir )))
1103 	    pspace[pcnt++] = pd2;
1104     }
1105 
1106     if ( pcnt==0 )
1107 return( NULL );
1108     if ( pcnt==1 ) {
1109 	/* if the line consists of just these two points, only count it as */
1110 	/*  a true line if the two immediately follow each other */
1111 	if (( pd->sp->next->to != pspace[0]->sp || !pd->sp->next->knownlinear ) &&
1112 	    ( pd->sp->prev->from != pspace[0]->sp || !pd->sp->prev->knownlinear ))
1113 return( NULL );
1114     }
1115 
1116     line = &gd->lines[gd->linecnt++];
1117     line->pcnt = pcnt+1;
1118     line->points = malloc((pcnt+1)*sizeof(struct pointdata *));
1119     line->points[0] = pd;
1120     line->unit = *dir;
1121     line->is_left = is_l;
1122     if ( dir->x < 0 || dir->y == -1 ) {
1123 	line->unit.x = -line->unit.x;
1124 	line->unit.y = -line->unit.y;
1125     }
1126     line->online = *base;
1127     if ( is_next ) {
1128 	pd->nextline = line;
1129 	if ( pd->colinear ) pd->prevline = line;
1130     } else {
1131 	pd->prevline = line;
1132 	if ( pd->colinear ) pd->nextline = line;
1133     }
1134     for ( i=0; i<pcnt; ++i ) {
1135 	if ( UnitsParallel( dir,&pspace[i]->nextunit,true ) && pspace[i]->nextline==NULL ) {
1136 	    pspace[i]->nextline = line;
1137 	    if ( pspace[i]->colinear )
1138 		pspace[i]->prevline = line;
1139 	}
1140 	if ( UnitsParallel( dir,&pspace[i]->prevunit,true ) && pspace[i]->prevline==NULL ) {
1141 	    pspace[i]->prevline = line;
1142 	    if ( pspace[i]->colinear )
1143 		pspace[i]->nextline = line;
1144 	}
1145 	line->points[i+1] = pspace[i];
1146     }
1147     qsort( line->points,line->pcnt,sizeof( struct pointdata * ),line_pt_cmp );
1148     start = &line->points[0]->sp->me;
1149     end = &line->points[pcnt]->sp->me;
1150     /* Now recalculate the line unit vector basing on its starting and */
1151     /* terminal points */
1152     line->unit.x = ( end->x - start->x );
1153     line->unit.y = ( end->y - start->y );
1154     line->length = sqrt( pow( line->unit.x,2 ) + pow( line->unit.y,2 ));
1155     line->unit.x /= line->length;
1156     line->unit.y /= line->length;
1157     hv = IsUnitHV( &line->unit,true );
1158     if ( hv == 2 ) {
1159 	line->unit.x = 0; line->unit.y = 1;
1160     } else if ( hv == 1 ) {
1161 	line->unit.x = 1; line->unit.y = 0;
1162     } else if ( gd->has_slant && UnitsParallel( &line->unit,&gd->slant_unit,true )) {
1163 	firstoff =  ( start->x - base->x )*gd->slant_unit.y -
1164 		    ( start->y - base->y )*gd->slant_unit.x;
1165 	lastoff =   ( end->x - base->x )*gd->slant_unit.y -
1166 		    ( end->y - base->y )*gd->slant_unit.x;
1167 	if ( fabs( firstoff ) < 2*dist_error && fabs( lastoff ) < 2*dist_error )
1168 	    line->unit = gd->slant_unit;
1169     }
1170 return( line );
1171 }
1172 
MiddleUnit(BasePoint * unit1,BasePoint * unit2)1173 static BasePoint MiddleUnit( BasePoint *unit1, BasePoint *unit2 ) {
1174     BasePoint u1, u2, ret;
1175     double hyp;
1176     int hv;
1177 
1178     u1 = *unit1; u2 = *unit2;
1179     if ( u1.x*u2.x + u1.y*u2.y < 0 ) {
1180 	u2.x = -u2.x; u2.y = -u2.y;
1181     }
1182     ret.x = ( u1.x + u2.x )/2;
1183     ret.y = ( u1.y + u2.y )/2;
1184     hyp = sqrt( pow( ret.x,2 ) + pow( ret.y,2 ));
1185     ret.x /= hyp;
1186     ret.y /= hyp;
1187 
1188     hv = IsUnitHV( &ret,true );
1189     if ( hv ) {
1190 	ret.x = ( hv == 1 ) ? 1 : 0;
1191 	ret.y = ( hv == 1 ) ? 0 : 1;
1192     }
1193 return( ret );
1194 }
1195 
IsStubOrIntersection(struct glyphdata * gd,BasePoint * dir1,struct pointdata * pd1,struct pointdata * pd2,int is_next1,int is_next2)1196 static uint8 IsStubOrIntersection( struct glyphdata *gd, BasePoint *dir1,
1197     struct pointdata *pd1, struct pointdata *pd2, int is_next1, int is_next2 ) {
1198     int i;
1199     int exc=0;
1200     double dist, off, ext, norm1, norm2, opp, angle;
1201     double mid_err = ( stem_slope_error + stub_slope_error )/2;
1202     SplinePoint *sp1, *sp2, *nsp;
1203     BasePoint hvdir, *dir2, *odir1, *odir2;
1204     struct pointdata *npd;
1205     struct linedata *line;
1206 
1207     sp1 = pd1->sp; sp2 = pd2->sp;
1208     dir2 = ( is_next2 ) ? &pd2->nextunit : &pd2->prevunit;
1209     hvdir.x = ( int ) rint( dir1->x );
1210     hvdir.y = ( int ) rint( dir1->y );
1211 
1212     line = is_next2 ? pd2->nextline : pd2->prevline;
1213     if ( !IsUnitHV( dir2,true ) && line != NULL )
1214 	dir2 = &line->unit;
1215 
1216     odir1 = ( is_next1 ) ? &pd1->prevunit : &pd1->nextunit;
1217     odir2 = ( is_next2 ) ? &pd2->prevunit : &pd2->nextunit;
1218 
1219     angle = fabs( GetUnitAngle( dir1,dir2 ));
1220     if ( angle > (double)stub_slope_error*1.5 && angle < FF_PI - (double)stub_slope_error*1.5 )
1221 return( 0 );
1222 
1223     /* First check if it is a slightly slanted line or a curve which joins */
1224     /* a straight line under an angle close to 90 degrees. There are many */
1225     /* glyphs where circles or curved features are intersected by or */
1226     /* connected to vertical or horizontal straight stems (the most obvious */
1227     /* cases are Greek Psi and Cyrillic Yu), and usually it is highly desired to */
1228     /* mark such an intersection with a hint */
1229     norm1 = ( sp1->me.x - sp2->me.x ) * odir2->x +
1230 	    ( sp1->me.y - sp2->me.y ) * odir2->y;
1231     norm2 = ( sp2->me.x - sp1->me.x ) * odir1->x +
1232 	    ( sp2->me.y - sp1->me.y ) * odir1->y;
1233     /* if this is a real stub or intersection, then vectors on both sides */
1234     /* of out going-to-be stem should point in the same direction. So */
1235     /* the following value should be positive */
1236     opp = dir1->x * dir2->x + dir1->y * dir2->y;
1237     if (( angle <= mid_err || angle >= FF_PI - mid_err ) &&
1238 	opp > 0 && norm1 < 0 && norm2 < 0 && UnitsParallel( odir1,odir2,true ) &&
1239 	( UnitsOrthogonal( dir1,odir1,false ) || UnitsOrthogonal( dir2,odir1,false )))
1240 return( 2 );
1241     if (( angle <= mid_err || angle >= FF_PI - mid_err ) &&
1242 	opp > 0 && (( norm1 < 0 && pd1->colinear &&
1243 	IsUnitHV( dir1,true ) && UnitsOrthogonal( dir1,odir2,false )) ||
1244 	( norm2 < 0 && pd2->colinear &&
1245 	IsUnitHV( dir2,true ) && UnitsOrthogonal( dir2,odir1,false ))))
1246 return( 4 );
1247 
1248     /* Now check if our 2 points form a serif termination or a feature stub */
1249     /* The check is pretty dumb: it returns 'true' if all the following */
1250     /* conditions are met: */
1251     /* - both the points belong to the same contour; */
1252     /* - there are no more than 3 other points between them; */
1253     /* - anyone of those intermediate points is positioned by such a way */
1254     /*   that it falls inside the stem formed by our 2 base point and */
1255     /*   the vector we are checking and its distance from the first point */
1256     /*   along that vector is not larger than the stem width; */
1257     /* - none of the intermediate points is parallel to the vector direction */
1258     /*   (otherwise we should have checked against that point instead) */
1259     if ( !UnitsParallel( dir1,&hvdir,false ))
1260 return( 0 );
1261 
1262     dist = ( sp1->me.x - sp2->me.x ) * dir1->y -
1263 	   ( sp1->me.y - sp2->me.y ) * dir1->x;
1264     nsp = sp1;
1265 
1266     for ( i=0; i<4; i++ ) {
1267 	if (( is_next1 && nsp->prev == NULL ) || ( !is_next1 && nsp->next == NULL ))
1268 return( 0 );
1269 
1270 	nsp = ( is_next1 ) ? nsp->prev->from : nsp->next->to;
1271 	if ( ( i>0 && nsp == sp1 ) || nsp == sp2 )
1272     break;
1273 
1274 	npd = &gd->points[nsp->ptindex];
1275 	if (UnitsParallel( &npd->nextunit,&hvdir,false ) ||
1276 	    UnitsParallel( &npd->prevunit,&hvdir,false ))
1277     break;
1278 
1279 	ext = ( sp1->me.x - nsp->me.x ) * hvdir.x +
1280 	      ( sp1->me.y - nsp->me.y ) * hvdir.y;
1281 	if ( ext < 0 ) ext = -ext;
1282 	if (( dist > 0 && ext > dist ) || ( dist < 0 && ext < dist ))
1283     break;
1284 
1285 	off = ( sp1->me.x - nsp->me.x ) * hvdir.y -
1286 	      ( sp1->me.y - nsp->me.y ) * hvdir.x;
1287 	if (( dist > 0 && ( off <= 0 || off >= dist )) ||
1288 	    ( dist < 0 && ( off >= 0 || off <= dist )))
1289 	    exc++;
1290     }
1291 
1292     if ( nsp == sp2 && exc == 0 )
1293 return( 1 );
1294 
1295 return( 0 );
1296 }
1297 
1298 /* We normalize all stem unit vectors so that they point between 90 and 270    */
1299 /* degrees, as this range is optimal for sorting diagonal stems. This means    */
1300 /* that vertical stems will normally point top to bottom, but for diagonal     */
1301 /* stems (even if their angle is actually very close to vertical) the opposite */
1302 /* direction is also possible. Sometimes we "normalize" such stems converting  */
1303 /* them to vertical. In such a case we have to swap their edges too.  */
SwapEdges(struct glyphdata * gd,struct stemdata * stem)1304 static void SwapEdges( struct glyphdata *gd,struct stemdata *stem ) {
1305     BasePoint tpos;
1306     struct pointdata *tpd;
1307     struct linedata *tl;
1308     struct stem_chunk *chunk;
1309     double toff;
1310     int i, j, temp;
1311 
1312     tpos = stem->left; stem->left = stem->right; stem->right = tpos;
1313     toff = stem->lmin; stem->lmin = stem->rmax; stem->rmax = toff;
1314     toff = stem->rmin; stem->rmin = stem->lmax; stem->lmax = toff;
1315     tl = stem->leftline; stem->leftline = stem->rightline; stem->rightline = tl;
1316 
1317     for ( i=0; i<stem->chunk_cnt; ++i ) {
1318 	chunk = &stem->chunks[i];
1319 	tpd = chunk->l; chunk->l = chunk->r; chunk->r = tpd;
1320 	temp = chunk->lpotential; chunk->lpotential = chunk->rpotential; chunk->rpotential = temp;
1321 	temp = chunk->lnext; chunk->lnext = chunk->rnext; chunk->rnext = temp;
1322 	temp = chunk->ltick; chunk->ltick = chunk->rtick; chunk->rtick = temp;
1323 
1324 	tpd = chunk->l;
1325 	if ( tpd != NULL ) {
1326 	    for ( j=0; j<tpd->nextcnt; j++ )
1327 		if ( tpd->nextstems[j] == stem )
1328 		    tpd->next_is_l[j] = true;
1329 	    for ( j=0; j<tpd->prevcnt; j++ )
1330 		if ( tpd->prevstems[j] == stem )
1331 		    tpd->prev_is_l[j] = true;
1332 	}
1333 
1334 	tpd = chunk->r;
1335 	if ( tpd != NULL ) {
1336 	    for ( j=0; j<tpd->nextcnt; j++ )
1337 		if ( tpd->nextstems[j] == stem )
1338 		    tpd->next_is_l[j] = false;
1339 	    for ( j=0; j<tpd->prevcnt; j++ )
1340 		if ( tpd->prevstems[j] == stem )
1341 		    tpd->prev_is_l[j] = false;
1342 	}
1343     }
1344 
1345     /* In case of a quadratic contour invert assignments to stem sides */
1346     /* also for off-curve points */
1347     if ( gd->order2 ) {
1348 	for ( i=0; i<gd->realcnt; i++ ) if ( gd->points[i].sp == NULL ) {
1349 	    tpd = &gd->points[i];
1350 	    for ( j=0; j<tpd->nextcnt; j++ )
1351 		if ( tpd->nextstems[j] == stem )
1352 		    tpd->next_is_l[j] = !tpd->next_is_l[j];
1353 	    for ( j=0; j<tpd->prevcnt; j++ )
1354 		if ( tpd->prevstems[j] == stem )
1355 		    tpd->prev_is_l[j] = !tpd->prev_is_l[j];
1356 	}
1357     }
1358 }
1359 
StemFitsHV(struct stemdata * stem,int is_x,uint8 mask)1360 static int StemFitsHV( struct stemdata *stem,int is_x,uint8 mask ) {
1361     int i,cnt;
1362     double loff,roff;
1363     double lmin=0,lmax=0,rmin=0,rmax=0;
1364     struct stem_chunk *chunk;
1365 
1366     cnt = stem->chunk_cnt;
1367 
1368     for ( i=0 ; i<stem->chunk_cnt; i++ ) {
1369 	if( stem->chunks[i].stub & mask )
1370     break;
1371     }
1372     if ( i == stem->chunk_cnt )
1373 return( false );
1374     if ( stem->chunk_cnt == 1 )
1375 return( true );
1376 
1377     for ( i=0;i<cnt;i++ ) {
1378 	chunk = &stem->chunks[i];
1379 
1380 	if ( chunk->l != NULL ) {
1381 	    loff = ( chunk->l->sp->me.x - stem->left.x ) * !is_x -
1382 		   ( chunk->l->sp->me.y - stem->left.y ) * is_x;
1383 	    if ( loff < lmin ) lmin = loff;
1384 	    else if ( loff > lmax ) lmax = loff;
1385 	}
1386 	if ( chunk->r != NULL ) {
1387 	    roff = ( chunk->r->sp->me.x - stem->right.x ) * !is_x -
1388 		   ( chunk->r->sp->me.y - stem->right.y ) * is_x;
1389 	    if ( roff < rmin ) rmin = roff;
1390 	    else if ( roff > rmax ) rmax = roff;
1391 	}
1392     }
1393     if ((( lmax - lmin ) < 2*dist_error_hv ) && (( rmax - rmin ) < 2*dist_error_hv ))
1394 return( true );
1395 return( false );
1396 }
1397 
LineFitsHV(struct linedata * line)1398 static int LineFitsHV( struct linedata *line ) {
1399     int i,cnt,is_x,hv;
1400     double off,min=0,max=0;
1401     struct pointdata *pd;
1402 
1403     cnt = line->pcnt;
1404     hv = IsUnitHV( &line->unit,true );
1405     if ( hv )
1406 return( true );
1407 
1408     hv = IsUnitHV( &line->unit,false );
1409     if ( !hv )
1410 return( false );
1411 
1412     is_x = ( hv == 1 ) ? 1 : 0;
1413     for ( i=0;i<cnt;i++ ) {
1414 	pd = line->points[i];
1415 
1416 	off =   ( pd->base.x - line->online.x ) * !is_x -
1417 		( pd->base.y - line->online.y ) * is_x;
1418 	if ( off < min ) min = off;
1419 	else if ( off > max ) max = off;
1420     }
1421     if (( max - min ) < 2*dist_error_hv )
1422 return( true );
1423 return( false );
1424 }
1425 
OnStem(struct stemdata * stem,BasePoint * test,int left)1426 static int OnStem( struct stemdata *stem,BasePoint *test,int left ) {
1427     double dist_error, off;
1428     BasePoint *dir = &stem->unit;
1429     double max=0, min=0;
1430 
1431     /* Diagonals are harder to align */
1432     dist_error = IsUnitHV( dir,true ) ? dist_error_hv : dist_error_diag;
1433     if ( !stem->positioned ) dist_error = dist_error * 2;
1434     if ( dist_error > stem->width/2 ) dist_error = stem->width/2;
1435     if ( left ) {
1436 	off = (test->x - stem->left.x)*dir->y - (test->y - stem->left.y)*dir->x;
1437 	max = stem->lmax; min = stem->lmin;
1438     } else {
1439 	off = (test->x - stem->right.x)*dir->y - (test->y - stem->right.y)*dir->x;
1440 	max = stem->rmax; min = stem->rmin;
1441     }
1442 
1443     if ( off > ( max - dist_error ) && off < ( min + dist_error ) )
1444 return( true );
1445 
1446 return( false );
1447 }
1448 
BothOnStem(struct stemdata * stem,BasePoint * test1,BasePoint * test2,int force_hv,int strict,int cove)1449 static int BothOnStem( struct stemdata *stem,BasePoint *test1,BasePoint *test2,
1450     int force_hv,int strict,int cove ) {
1451     double dist_error, off1, off2;
1452     BasePoint dir = stem->unit;
1453     int hv, hv_strict;
1454     double lmax=0, lmin=0, rmax=0, rmin=0;
1455 
1456     hv = ( force_hv ) ? IsUnitHV( &dir,false ) : IsUnitHV( &dir,true );
1457     hv_strict = ( force_hv ) ? IsUnitHV( &dir,true ) : hv;
1458     if ( force_hv ) {
1459 	if ( force_hv != hv )
1460 return( false );
1461 	if ( !hv_strict && !StemFitsHV( stem,( hv == 1 ),7 ))
1462 return( false );
1463 	if ( !hv_strict ) {
1464 	    dir.x = ( force_hv == 2 ) ? 0 : 1;
1465 	    dir.y = ( force_hv == 2 ) ? 1 : 0;
1466 	}
1467     }
1468     /* Diagonals are harder to align */
1469     dist_error = ( hv ) ? dist_error_hv : dist_error_diag;
1470     if ( !strict ) {
1471 	dist_error = dist_error * 2;
1472 	lmax = stem->lmax; lmin = stem->lmin;
1473 	rmax = stem->rmax; rmin = stem->rmin;
1474     }
1475     if ( dist_error > stem->width/2 ) dist_error = stem->width/2;
1476 
1477     off1 = (test1->x-stem->left.x)*dir.y - (test1->y-stem->left.y)*dir.x;
1478     off2 = (test2->x-stem->right.x)*dir.y - (test2->y-stem->right.y)*dir.x;
1479     if (off1 > ( lmax - dist_error ) && off1 < ( lmin + dist_error ) &&
1480 	off2 > ( rmax - dist_error ) && off2 < ( rmin + dist_error )) {
1481 	/* For some reasons in my patch from Feb 24 2008 I prohibited snapping */
1482 	/* to stems point pairs which together form a bend, if at least */
1483 	/* one point from the pair doesn't have exactly the same position as */
1484 	/* the stem edge. Unfortunately I don't remember why I did this, but */
1485 	/* this behavior has at least one obviously negative effect: it */
1486 	/* prevents building a stem from chunks which describe an ark   */
1487 	/* intersected by some straight lines, even if the intersections lie */
1488 	/* closely enough to the ark extremum. So don't apply this test */
1489 	/* at least if the force_hv flag is on (which means either the  */
1490 	/* chunk or the stem itself is not exactly horizontal/vertical) */
1491 	if ( !cove || force_hv || off1 == 0 || off2 == 0 )
1492 return( true );
1493     }
1494 
1495     off2 = (test2->x-stem->left.x)*dir.y - (test2->y-stem->left.y)*dir.x;
1496     off1 = (test1->x-stem->right.x)*dir.y - (test1->y-stem->right.y)*dir.x;
1497     if (off2 > ( lmax - dist_error ) && off2 < ( lmin + dist_error ) &&
1498 	off1 > ( rmax - dist_error ) && off1 < ( rmin + dist_error )) {
1499 	if ( !cove || force_hv || off1 == 0 || off2 == 0 )
1500 return( true );
1501     }
1502 
1503 return( false );
1504 }
1505 
RecalcStemOffsets(struct stemdata * stem,BasePoint * dir,int left,int right)1506 static int RecalcStemOffsets( struct stemdata *stem,BasePoint *dir,int left,int right ) {
1507     double off, err;
1508     double lmin=0, lmax=0, rmin=0, rmax=0;
1509     struct stem_chunk *chunk;
1510     int i;
1511 
1512     if ( !left && !right )
1513 return( false );
1514     err = ( IsUnitHV( dir,true )) ? dist_error_hv : dist_error_diag;
1515 
1516     if ( stem->chunk_cnt > 1 ) for ( i=0; i<stem->chunk_cnt; i++ ) {
1517 	chunk = &stem->chunks[i];
1518 	if ( left && chunk->l != NULL ) {
1519 	    off =  ( chunk->l->sp->me.x - stem->left.x )*dir->y -
1520 		   ( chunk->l->sp->me.y - stem->left.y )*dir->x;
1521 	    if ( off < lmin ) lmin = off;
1522 	    else if ( off > lmax ) lmax = off;
1523 	}
1524 	if ( right && chunk->r != NULL ) {
1525 	    off =  ( chunk->r->sp->me.x - stem->right.x )*dir->y +
1526 		   ( chunk->r->sp->me.y - stem->right.y )*dir->x;
1527 	    if ( off < rmin ) rmin = off;
1528 	    else if ( off > rmax ) rmax = off;
1529 	}
1530     }
1531     if ( lmax - lmin < 2*err && rmax - rmin < 2*err ) {
1532 	stem->lmin = lmin; stem->lmax = lmax;
1533 	stem->rmin = rmin; stem->rmax = rmax;
1534 return( true );
1535     }
1536 return( false );
1537 }
1538 
SetStemUnit(struct stemdata * stem,BasePoint dir)1539 static void SetStemUnit( struct stemdata *stem,BasePoint dir ) {
1540     double width;
1541 
1542     width = ( stem->right.x - stem->left.x ) * dir.y -
1543 	    ( stem->right.y - stem->left.y ) * dir.x;
1544     if ( width < 0 ) {
1545 	width = -width;
1546 	dir.x = -dir.x;
1547 	dir.y = -dir.y;
1548     }
1549     stem->unit = dir;
1550     stem->width = width;
1551 
1552     /* Guess at which normal we want */
1553     stem->l_to_r.x = dir.y; stem->l_to_r.y = -dir.x;
1554     /* If we guessed wrong, use the other */
1555     if (( stem->right.x-stem->left.x )*stem->l_to_r.x +
1556 	( stem->right.y-stem->left.y )*stem->l_to_r.y < 0 ) {
1557 	stem->l_to_r.x = -stem->l_to_r.x;
1558 	stem->l_to_r.y = -stem->l_to_r.y;
1559     }
1560 
1561     /* Recalculate left/right offsets relatively to new vectors */
1562     RecalcStemOffsets( stem,&dir,true,true );
1563 }
1564 
AddToStem(struct glyphdata * gd,struct stemdata * stem,struct pointdata * pd1,struct pointdata * pd2,int is_next1,int is_next2,int cheat)1565 static struct stem_chunk *AddToStem( struct glyphdata *gd,struct stemdata *stem,
1566     struct pointdata *pd1,struct pointdata *pd2,int is_next1, int is_next2, int cheat ) {
1567 
1568     int is_potential1 = false, is_potential2 = true;
1569     struct stem_chunk *chunk=NULL;
1570     BasePoint *dir = &stem->unit;
1571     BasePoint *test;
1572     int lincr = 1, rincr = 1;
1573     double off, dist_error;
1574     double loff = 0, roff = 0;
1575     double min = 0, max = 0;
1576     int i, in, ip, cpidx;
1577     struct pointdata *pd, *npd, *ppd;
1578 
1579     if ( cheat || stem->positioned ) is_potential2 = false;
1580     /* Diagonals are harder to align */
1581     dist_error = IsUnitHV( dir,true ) ? 2*dist_error_hv : 2*dist_error_diag;
1582     if ( dist_error > stem->width/2 ) dist_error = stem->width/2;
1583     max = stem->lmax;
1584     min = stem->lmin;
1585 
1586     /* The following swaps "left" and "right" points in case we have */
1587     /* started checking relatively to a wrong edge */
1588     if ( pd1 != NULL ) {
1589 	test = &pd1->base;
1590 	off =   ( test->x - stem->left.x )*dir->y -
1591 		( test->y - stem->left.y )*dir->x;
1592 	if (( !stem->ghost &&
1593 	    ( off < ( max - dist_error ) || off > ( min + dist_error ))) ||
1594 	    ( RealNear( stem->unit.x, 1) && stem->ghost && stem->width == 21 ) ||
1595 	    ( RealNear( stem->unit.x,0 ) && stem->ghost && stem->width == 20 )) {
1596 	    pd = pd1; pd1 = pd2; pd2 = pd;
1597 	    in = is_next1; is_next1 = is_next2; is_next2 = in;
1598 	    ip = is_potential1; is_potential1 = is_potential2; is_potential2 = ip;
1599 	}
1600     }
1601 
1602     if ( pd1 == NULL ) lincr = 0;
1603     if ( pd2 == NULL ) rincr = 0;
1604     /* Now run through existing stem chunks and see if the chunk we are */
1605     /* going to add doesn't duplicate an existing one.*/
1606     for ( i=stem->chunk_cnt-1; i>=0; --i ) {
1607 	chunk = &stem->chunks[i];
1608 	if ( chunk->l == pd1 ) lincr = 0;
1609 	if ( chunk->r == pd2 ) rincr = 0;
1610 
1611 	if (( chunk->l == pd1 || pd1 == NULL ) && ( chunk->r == pd2 || pd2 == NULL )) {
1612 	    if ( !is_potential1 ) chunk->lpotential = false;
1613 	    if ( !is_potential2 ) chunk->rpotential = false;
1614     break;
1615 	} else if (( chunk->l == pd1 && chunk->r == NULL ) || ( chunk->r == pd2 && chunk->l == NULL )) {
1616 	    if ( chunk->l == NULL ) {
1617 		chunk->l = pd1;
1618 		chunk->lpotential = is_potential1;
1619 		chunk->lnext = is_next1;
1620 		chunk->ltick = lincr;
1621 	    } else if ( chunk->r == NULL ) {
1622 		chunk->r = pd2;
1623 		chunk->rpotential = is_potential2;
1624 		chunk->rnext = is_next2;
1625 		chunk->rtick = rincr;
1626 	    }
1627     break;
1628 	}
1629     }
1630 
1631     if ( i<0 ) {
1632 	stem->chunks = realloc(stem->chunks,(stem->chunk_cnt+1)*sizeof(struct stem_chunk));
1633 	chunk = &stem->chunks[stem->chunk_cnt++];
1634 	chunk->parent = stem;
1635 
1636 	chunk->l = pd1; chunk->lpotential = is_potential1;
1637 	chunk->r = pd2; chunk->rpotential = is_potential2;
1638 	chunk->ltick = lincr; chunk->rtick = rincr;
1639 
1640 	chunk->lnext = is_next1;
1641 	chunk->rnext = is_next2;
1642 	chunk->stemcheat = cheat;
1643 	chunk->stub = chunk->is_ball = false;
1644 	chunk->l_e_idx = chunk->r_e_idx = 0;
1645     }
1646 
1647     if ( pd1!=NULL ) {
1648 	loff =  ( pd1->base.x - stem->left.x ) * stem->l_to_r.x +
1649 		( pd1->base.y - stem->left.y ) * stem->l_to_r.y;
1650 	if ( is_next1==1 || is_next1==2 || pd1->colinear ) {
1651 	    AssignStemToPoint( pd1,stem,true,true );
1652 	    /* For quadratic layers assign the stem not only to   */
1653 	    /* spline points, but to their control points as well */
1654 	    /* (this may be important for TTF instructions */
1655 	    if ( gd->order2 && !pd1->sp->nonextcp && pd1->sp->nextcpindex < gd->realcnt ) {
1656 		cpidx = pd1->sp->nextcpindex;
1657 		npd = &gd->points[cpidx];
1658 		if ( OnStem( stem,&npd->base,true ))
1659 		    AssignStemToPoint( npd,stem,false,true );
1660 	    }
1661 	}
1662 	if ( is_next1==0 || is_next1==2 || pd1->colinear  ) {
1663 	    AssignStemToPoint( pd1,stem,false,true );
1664 	    if ( gd->order2 && !pd1->sp->noprevcp && pd1->sp->prev != NULL &&
1665 		pd1->sp->prev->from->nextcpindex < gd->realcnt ) {
1666 		cpidx = pd1->sp->prev->from->nextcpindex;
1667 		ppd = &gd->points[cpidx];
1668 		if ( OnStem( stem,&ppd->base,true ))
1669 		    AssignStemToPoint( ppd,stem,true,true );
1670 	    }
1671 	}
1672     }
1673     if ( pd2!=NULL ) {
1674 	roff =  ( pd2->base.x - stem->right.x ) * stem->l_to_r.x +
1675 		( pd2->base.y - stem->right.y ) * stem->l_to_r.y;
1676 	if ( is_next2==1 || is_next2==2 || pd2->colinear ) {
1677 	    AssignStemToPoint( pd2,stem,true,false );
1678 	    if ( gd->order2 && !pd2->sp->nonextcp && pd2->sp->nextcpindex < gd->realcnt ) {
1679 		cpidx = pd2->sp->nextcpindex;
1680 		npd = &gd->points[cpidx];
1681 		if ( OnStem( stem,&npd->base,false ))
1682 		    AssignStemToPoint( npd,stem,false,false );
1683 	    }
1684 	}
1685 	if ( is_next2==0 || is_next2==2 || pd2->colinear ) {
1686 	    AssignStemToPoint( pd2,stem,false,false );
1687 	    if ( gd->order2 && !pd2->sp->noprevcp && pd2->sp->prev != NULL &&
1688 		pd2->sp->prev->from->nextcpindex < gd->realcnt ) {
1689 		cpidx = pd2->sp->prev->from->nextcpindex;
1690 		ppd = &gd->points[cpidx];
1691 		if ( OnStem( stem,&ppd->base,false ))
1692 		    AssignStemToPoint( ppd,stem,true,false );
1693 	    }
1694 	}
1695     }
1696     if ( loff < stem->lmin ) stem->lmin = loff;
1697     else if ( loff > stem->lmax ) stem->lmax = loff;
1698     if ( roff < stem->rmin ) stem->rmin = roff;
1699     else if ( roff > stem->rmax ) stem->rmax = roff;
1700     stem->lpcnt += lincr; stem->rpcnt += rincr;
1701 return( chunk );
1702 }
1703 
FindStem(struct glyphdata * gd,struct pointdata * pd,struct pointdata * pd2,BasePoint * dir,int is_next2,int de)1704 static struct stemdata *FindStem( struct glyphdata *gd,struct pointdata *pd,
1705     struct pointdata *pd2,BasePoint *dir,int is_next2,int de ) {
1706 
1707     int i, cove, test_left, hv, stemcnt;
1708     struct stemdata *stem;
1709     SplinePoint *match;
1710     BasePoint newdir;
1711 
1712     match = pd2->sp;
1713     stemcnt = ( is_next2 ) ? pd2->nextcnt : pd2->prevcnt;
1714 
1715     for ( i=0; i<stemcnt; i++ ) {
1716 	stem = ( is_next2 ) ? pd2->nextstems[i] : pd2->prevstems[i];
1717 	test_left = ( is_next2 ) ? !pd2->next_is_l[i] : !pd2->prev_is_l[i];
1718 
1719 	if (UnitsParallel( &stem->unit,dir,true ) &&
1720 	    OnStem( stem,&pd->sp->me,test_left ))
1721 return( stem );
1722     }
1723 
1724     cove =  ( dir->x == 0 && pd->x_extr + pd2->x_extr == 3 ) ||
1725 	    ( dir->y == 0 && pd->y_extr + pd2->y_extr == 3 );
1726 
1727     /* First pass to check for strict matches */
1728     for ( i=0; i<gd->stemcnt; ++i ) {
1729 	stem = &gd->stems[i];
1730 	/* Ghost hints and BBox hits are usually generated after all other   */
1731 	/* hint types, but we can get them here in case we are generating    */
1732 	/* glyph data for a predefined hint layout. In this case they should */
1733 	/* be excluded from the following tests */
1734 	if ( stem->ghost || stem->bbox )
1735     continue;
1736 
1737 	if ( UnitsParallel( &stem->unit,dir,true ) &&
1738 	    BothOnStem( stem,&pd->sp->me,&pd2->sp->me,false,true,cove )) {
1739  return( stem );
1740 	}
1741     }
1742     /* One more pass. At this stage larger deviations are allowed */
1743     for ( i=0; i<gd->stemcnt; ++i ) {
1744 	stem = &gd->stems[i];
1745 	if ( stem->ghost || stem->bbox )
1746     continue;
1747 
1748 	if ( UnitsParallel( &stem->unit,dir,true ) &&
1749 	    BothOnStem( stem,&pd->sp->me,&pd2->sp->me,false,false,cove )) {
1750 return( stem );
1751 	}
1752     }
1753     if ( de )
1754 return( NULL );
1755 
1756     hv = IsUnitHV( dir,false );
1757     if ( !hv )
1758 return( NULL );
1759 
1760     for ( i=0; i<gd->stemcnt; ++i ) {
1761 	stem = &gd->stems[i];
1762 	if ( stem->ghost || stem->bbox )
1763     continue;
1764 	if ( hv && BothOnStem( stem,&pd->base,&pd2->base,hv,false,cove )) {
1765 	    newdir.x = ( hv == 2 ) ? 0 : 1;
1766 	    newdir.y = ( hv == 2 ) ? 1 : 0;
1767 	    if ( hv == 2 && stem->unit.y < 0 )
1768 		SwapEdges( gd,stem );
1769 	    if ( stem->unit.x != newdir.x )
1770 		SetStemUnit( stem,newdir );
1771 return( stem );
1772 	}
1773     }
1774 return( NULL );
1775 }
1776 
NewStem(struct glyphdata * gd,BasePoint * dir,BasePoint * pos1,BasePoint * pos2)1777 static struct stemdata *NewStem( struct glyphdata *gd,BasePoint *dir,
1778     BasePoint *pos1, BasePoint *pos2 ) {
1779 
1780     struct stemdata * stem = &gd->stems[gd->stemcnt++];
1781     double width;
1782 
1783     stem->unit = *dir;
1784     if ( dir->x < 0 || dir->y == -1 ) {
1785 	stem->unit.x = -stem->unit.x;
1786 	stem->unit.y = -stem->unit.y;
1787     }
1788     width = ( pos2->x - pos1->x ) * stem->unit.y -
1789 	    ( pos2->y - pos1->y ) * stem->unit.x;
1790     if ( width > 0 ) {
1791 	stem->left = *pos1;
1792 	stem->right = *pos2;
1793 	stem->width = width;
1794     } else {
1795 	stem->left = *pos2;
1796 	stem->right = *pos1;
1797 	stem->width = -width;
1798     }
1799     /* Guess at which normal we want */
1800     stem->l_to_r.x = dir->y; stem->l_to_r.y = -dir->x;
1801     /* If we guessed wrong, use the other */
1802     if (( stem->right.x-stem->left.x )*stem->l_to_r.x +
1803 	( stem->right.y-stem->left.y )*stem->l_to_r.y < 0 ) {
1804 	stem->l_to_r.x = -stem->l_to_r.x;
1805 	stem->l_to_r.y = -stem->l_to_r.y;
1806     }
1807     stem->leftidx = stem->rightidx = -1;
1808     stem->leftline = stem->rightline = NULL;
1809     stem->lmin = stem->lmax = 0;
1810     stem->rmin = stem->rmax = 0;
1811     stem->ldone = stem->rdone = false;
1812     stem->lpcnt = stem->rpcnt = 0;
1813     stem->chunks = NULL;
1814     stem->chunk_cnt = 0;
1815     stem->ghost = stem->bbox = false;
1816     stem->positioned = false;
1817     stem->blue = -1;
1818 return( stem );
1819 }
1820 
ParallelToDir(struct pointdata * pd,int checknext,BasePoint * dir,BasePoint * opposite,SplinePoint * basesp,uint8 is_stub)1821 static int ParallelToDir( struct pointdata *pd,int checknext,BasePoint *dir,
1822     BasePoint *opposite,SplinePoint *basesp,uint8 is_stub ) {
1823 
1824     BasePoint n, o, *base = &basesp->me;
1825     SplinePoint *sp;
1826     double angle, mid_err = ( stem_slope_error + stub_slope_error )/2;
1827 
1828     sp = pd->sp;
1829     n = ( checknext ) ? pd->nextunit : pd->prevunit;
1830 
1831     angle = fabs( GetUnitAngle( dir,&n ));
1832     if (( !is_stub && angle > stem_slope_error && angle < FF_PI - stem_slope_error ) ||
1833 	( is_stub & 1 && angle > stub_slope_error*1.5 && angle < FF_PI - stub_slope_error*1.5 ) ||
1834 	( is_stub & 6 && angle > mid_err && angle < FF_PI - mid_err ))
1835 return( false );
1836 
1837     /* Now sp must be on the same side of the spline as opposite */
1838     o.x = opposite->x-base->x; o.y = opposite->y-base->y;
1839     n.x = sp->me.x-base->x; n.y = sp->me.y-base->y;
1840     if ( ( o.x*dir->y - o.y*dir->x )*( n.x*dir->y - n.y*dir->x ) < 0 )
1841 return( false );
1842 
1843 return( true );
1844 }
1845 
NearlyParallel(BasePoint * dir,Spline * other,double t)1846 static int NearlyParallel( BasePoint *dir,Spline *other, double t ) {
1847     BasePoint odir;
1848     double olen;
1849 
1850     odir.x = (3*other->splines[0].a*t+2*other->splines[0].b)*t+other->splines[0].c;
1851     odir.y = (3*other->splines[1].a*t+2*other->splines[1].b)*t+other->splines[1].c;
1852     olen = sqrt( pow( odir.x,2 ) + pow( odir.y,2 ));
1853     if ( olen==0 )
1854 return( false );
1855     odir.x /= olen; odir.y /= olen;
1856 return( UnitsParallel( dir,&odir,false ));
1857 }
1858 
NormalDist(BasePoint * to,BasePoint * from,BasePoint * perp)1859 static double NormalDist( BasePoint *to, BasePoint *from, BasePoint *perp ) {
1860     double len = (to->x-from->x)*perp->y - (to->y-from->y)*perp->x;
1861     if ( len<0 ) len = -len;
1862 return( len );
1863 }
1864 
FindOrMakeHVStem(struct glyphdata * gd,struct pointdata * pd,struct pointdata * pd2,int is_h,int require_existing)1865 static struct stemdata *FindOrMakeHVStem( struct glyphdata *gd,
1866     struct pointdata *pd,struct pointdata *pd2,int is_h,int require_existing ) {
1867     int i,cove = false;
1868     struct stemdata *stem;
1869     BasePoint dir;
1870 
1871     dir.x = ( is_h ) ? 1 : 0;
1872     dir.y = ( is_h ) ? 0 : 1;
1873     if ( pd2 != NULL )
1874 	cove =  ( dir.x == 0 && pd->x_extr + pd2->x_extr == 3 ) ||
1875 		( dir.y == 0 && pd->y_extr + pd2->y_extr == 3 );
1876 
1877     for ( i=0; i<gd->stemcnt; ++i ) {
1878 	stem = &gd->stems[i];
1879 	if ( IsUnitHV( &stem->unit,true ) &&
1880 	    ( pd2 != NULL && BothOnStem( stem,&pd->sp->me,&pd2->sp->me,false,false,cove )))
1881     break;
1882     }
1883     if ( i==gd->stemcnt ) stem=NULL;
1884 
1885     if ( stem == NULL && pd2 != NULL && !require_existing )
1886 	stem = NewStem( gd,&dir,&pd->sp->me,&pd2->sp->me );
1887 return( stem );
1888 }
1889 
IsDiagonalEnd(struct glyphdata * gd,struct pointdata * pd1,struct pointdata * pd2,int is_next,int require_existing)1890 static int IsDiagonalEnd( struct glyphdata *gd,
1891     struct pointdata *pd1,struct pointdata *pd2,int is_next,int require_existing ) {
1892     /* suppose we have something like */
1893     /*  *--*		*/
1894     /*   \  \		*/
1895     /*    \  \		*/
1896     /* Then let's create a vertical stem between the two points */
1897     /* (and a horizontal stem if the thing is rotated 90 degrees) */
1898     double width, length1, length2, dist1, dist2;
1899     BasePoint *pt1, *pt2, *dir1, *dir2, *prevdir1, *prevdir2;
1900     SplinePoint *prevsp1, *prevsp2;
1901     struct pointdata *prevpd1, *prevpd2;
1902     int hv;
1903 
1904     if ( pd1->colinear || pd2->colinear )
1905 return( false );
1906     pt1 = &pd1->sp->me; pt2 = &pd2->sp->me;
1907     /* Both key points of a diagonal end stem should have nearly the same */
1908     /* coordinate by x or y (otherwise we can't determine by which axis   */
1909     /* it should be hinted) */
1910     if ( pt1->x >= pt2->x - dist_error_hv &&  pt1->x <= pt2->x + dist_error_hv ) {
1911 	width = pd1->sp->me.y - pd2->sp->me.y;
1912 	hv = 1;
1913     } else if ( pt1->y >= pt2->y - dist_error_hv &&  pt1->y <= pt2->y + dist_error_hv ) {
1914 	width = pd1->sp->me.x - pd2->sp->me.x;
1915 	hv = 2;
1916     } else
1917 return( false );
1918 
1919     dir1 = ( is_next ) ? &pd1->nextunit : &pd1->prevunit;
1920     dir2 = ( is_next ) ? &pd2->prevunit : &pd2->nextunit;
1921     if ( IsUnitHV( dir1,true ))	/* Must be diagonal */
1922 return( false );
1923     prevsp1 = ( is_next ) ? pd1->sp->next->to : pd1->sp->prev->from;
1924     prevsp2 = ( is_next ) ? pd2->sp->prev->from : pd2->sp->next->to;
1925     prevpd1 = &gd->points[prevsp1->ptindex];
1926     prevpd2 = &gd->points[prevsp2->ptindex];
1927     prevdir1 = ( is_next ) ? &prevpd1->prevunit : &prevpd1->nextunit;
1928     prevdir2 = ( is_next ) ? &prevpd2->nextunit : &prevpd2->prevunit;
1929     /* Ensure we have got a real diagonal, i. e. its sides are parallel */
1930     if ( !UnitsParallel( dir1,dir2,true ) || !UnitsParallel( prevdir1,prevdir2,true ))
1931 return( false );
1932 
1933     /* Diagonal width should be smaller than its length */
1934     length1 = pow(( prevsp1->me.x - pt1->x ),2 ) + pow(( prevsp1->me.y - pt1->y ),2 );
1935     length2 = pow(( prevsp2->me.x - pt2->x ),2 ) + pow(( prevsp2->me.y - pt2->y ),2 );
1936     if ( length2 < length1 ) length1 = length2;
1937     if ( pow( width,2 ) > length1 )
1938 return( false );
1939 
1940     /* Finally exclude too short diagonals where the distance between key   */
1941     /* points of one edge at the direction orthogonal to the unit vector    */
1942     /* of the stem we are about to add is smaller than normal HV stem       */
1943     /* fudge. Such diagonals may be later turned into HV stems, and we will */
1944     /* result into getting two coincident hints */
1945     dist1 = ( hv == 1 ) ? prevsp1->me.y - pt1->y : prevsp1->me.x - pt1->x;
1946     dist2 = ( hv == 1 ) ? prevsp2->me.y - pt2->y : prevsp2->me.x - pt2->x;
1947     if ( dist1 < 0 ) dist1 = -dist1;
1948     if ( dist2 < 0 ) dist2 = -dist2;
1949     if ( dist1 < 2*dist_error_hv && dist2 < 2*dist_error_hv )
1950 return( false );
1951 
1952 return( hv );
1953 }
1954 
TestStem(struct glyphdata * gd,struct pointdata * pd,BasePoint * dir,SplinePoint * match,int is_next,int is_next2,int require_existing,uint8 is_stub,int eidx)1955 static struct stemdata *TestStem( struct glyphdata *gd,struct pointdata *pd,
1956     BasePoint *dir,SplinePoint *match,int is_next,int is_next2,int require_existing,uint8 is_stub,int eidx ) {
1957     struct pointdata *pd2;
1958     struct stemdata *stem, *destem;
1959     struct stem_chunk *chunk;
1960     struct linedata *otherline;
1961     double width;
1962     struct linedata *line, *line2;
1963     BasePoint *mdir, middle;
1964     int de=false, hv, l_changed;
1965 
1966     width = ( match->me.x - pd->sp->me.x )*dir->y -
1967 	    ( match->me.y - pd->sp->me.y )*dir->x;
1968     if ( width < 0 ) width = -width;
1969     if ( width < .5 )
1970 return( NULL );		/* Zero width stems aren't interesting */
1971     if (( is_next && pd->sp->next->to==match ) || ( !is_next && pd->sp->prev->from==match ))
1972 return( NULL );		/* Don't want a stem between two splines that intersect */
1973 
1974     pd2 = &gd->points[match->ptindex];
1975 
1976     line = is_next ? pd->nextline : pd->prevline;
1977     mdir = is_next2 ? &pd2->nextunit : &pd2->prevunit;
1978     line2 = is_next2 ? pd2->nextline : pd2->prevline;
1979     if ( !IsUnitHV( mdir,true ) && line2 != NULL )
1980 	mdir = &line2->unit;
1981     if ( mdir->x==0 && mdir->y==0 )
1982 return( NULL );         /* cannot determine the opposite point's direction */
1983 
1984     if ( !UnitsParallel( mdir,dir,true ) && !is_stub )
1985 return( NULL );         /* Cannot make a stem if edges are not parallel (unless it is a serif) */
1986 
1987     if ( is_stub & 1 && !IsUnitHV( dir,true )) {
1988 	/* For serifs we prefer the vector which is closer to horizontal/vertical */
1989 	middle = MiddleUnit( dir,mdir );
1990 	if ( UnitCloserToHV( &middle,dir ) == 1  && UnitCloserToHV( &middle,mdir ) == 1 )
1991 	    dir = &middle;
1992 	else if ( UnitCloserToHV( mdir,dir ) == 1 )
1993 	    dir = mdir;
1994 	if ( !IsUnitHV( dir,true ) &&
1995 	    ( hint_diagonal_ends || require_existing ))
1996 	    de = IsDiagonalEnd( gd,pd,pd2,is_next,require_existing );
1997     }
1998 
1999     stem = FindStem( gd,pd,pd2,dir,is_next2,de );
2000     destem = NULL;
2001     if ( de )
2002 	destem = FindOrMakeHVStem( gd,pd,pd2,( de == 1 ),require_existing );
2003 
2004     if ( stem == NULL && !require_existing )
2005 	stem = NewStem( gd,dir,&pd->sp->me,&match->me );
2006     if ( stem != NULL ) {
2007 	chunk = AddToStem( gd,stem,pd,pd2,is_next,is_next2,false );
2008 	if ( chunk != NULL ) {
2009 	    chunk->stub = is_stub;
2010 	    chunk->l_e_idx = chunk->r_e_idx = eidx;
2011 	}
2012 
2013 	if ( chunk != NULL && gd->linecnt > 0 ) {
2014 	    hv = IsUnitHV( &stem->unit,true );
2015 	    /* For HV stems allow assigning a line to a stem edge only */
2016 	    /* if that line also has an exactly HV vector */
2017 	    if ( line != NULL && (( !hv &&
2018 		UnitsParallel( &stem->unit,&line->unit,true ) &&
2019 		RecalcStemOffsets( stem,&line->unit,true,true )) ||
2020 		( hv && line->unit.x == stem->unit.x && line->unit.y == stem->unit.y ))) {
2021 
2022 		otherline = NULL; l_changed = false;
2023 		if (( stem->leftline == NULL ||
2024 		    stem->leftline->length < line->length ) && chunk->l == pd ) {
2025 
2026 		    stem->leftline = line;
2027 		    l_changed = true;
2028 		    otherline = stem->rightline;
2029 		} else if (( stem->rightline == NULL ||
2030 		    stem->rightline->length < line->length ) && chunk->r == pd ) {
2031 
2032 		    stem->rightline = line;
2033 		    l_changed = true;
2034 		    otherline = stem->leftline;
2035 		}
2036 		/* If lines are attached to both sides of a diagonal stem, */
2037 		/* then prefer the longer line */
2038 		if ( !hv && l_changed && !stem->positioned &&
2039 		    ( otherline == NULL || ( otherline->length < line->length )))
2040 		    SetStemUnit( stem,line->unit );
2041 	    }
2042 	    if ( line2 != NULL && (( !hv &&
2043 		UnitsParallel( &stem->unit,&line2->unit,true ) &&
2044 		RecalcStemOffsets( stem,&line2->unit,true,true )) ||
2045 		( hv && line2->unit.x == stem->unit.x && line2->unit.y == stem->unit.y ))) {
2046 
2047 		otherline = NULL; l_changed = false;
2048 		if (( stem->leftline == NULL ||
2049 		    stem->leftline->length < line2->length ) && chunk->l == pd2 ) {
2050 
2051 		    stem->leftline = line2;
2052 		    l_changed = true;
2053 		    otherline = stem->rightline;
2054 		} else if (( stem->rightline == NULL ||
2055 		    stem->rightline->length < line2->length ) && chunk->r == pd2 ) {
2056 
2057 		    stem->rightline = line2;
2058 		    l_changed = true;
2059 		    otherline = stem->leftline;
2060 		}
2061 		if ( !hv && l_changed && !stem->positioned &&
2062 		    ( otherline == NULL || ( otherline->length < line2->length )))
2063 		    SetStemUnit( stem,line2->unit );
2064 	    }
2065 	}
2066     }
2067 
2068     if ( destem != NULL )
2069 	AddToStem( gd,destem,pd,pd2,is_next,!is_next,1 );
2070 return( stem );
2071 }
2072 
FindSameSlope(Spline * s,BasePoint * dir,double close_to)2073 static double FindSameSlope(Spline *s,BasePoint *dir,double close_to) {
2074     double a, b, c, desc;
2075     double t1, t2;
2076     double d1, d2;
2077 
2078     if ( s==NULL )
2079 return( -1e4 );
2080 
2081     a = dir->x*s->splines[1].a*3 - dir->y*s->splines[0].a*3;
2082     b = dir->x*s->splines[1].b*2 - dir->y*s->splines[0].b*2;
2083     c = dir->x*s->splines[1].c   - dir->y*s->splines[0].c  ;
2084     if ( a!=0 ) {
2085 	desc = b*b - 4*a*c;
2086 	if ( desc<0 )
2087 return( -1e4 );
2088 	desc = sqrt(desc);
2089 	t1 = (-b+desc)/(2*a);
2090 	t2 = (-b-desc)/(2*a);
2091 	if ( (d1=t1-close_to)<0 ) d1 = -d1;
2092 	if ( (d2=t2-close_to)<0 ) d2 = -d2;
2093 	if ( d2<d1 && t2>=-.001 && t2<=1.001 )
2094 	    t1 = t2;
2095     } else if ( b!=0 )
2096 	t1 = -c/b;
2097     else
2098 return( -1e4 );
2099 
2100 return( t1 );
2101 }
2102 
2103 /* This function is used when generating stem data for preexisting */
2104 /* stem hints. If we already know the desired hint position, then we */
2105 /* can safely assign to this hint any points which meet other conditions */
2106 /* but have no corresponding position at the opposite edge. */
HalfStemNoOpposite(struct glyphdata * gd,struct pointdata * pd,struct stemdata * stem,BasePoint * dir,int is_next)2107 static int HalfStemNoOpposite( struct glyphdata *gd,struct pointdata *pd,
2108     struct stemdata *stem,BasePoint *dir,int is_next ) {
2109     int i, ret=0, allowleft, allowright, hv, corner;
2110     struct stemdata *tstem;
2111 
2112     for ( i=0; i<gd->stemcnt; ++i ) {
2113 	tstem = &gd->stems[i];
2114 	if ( tstem->bbox || !tstem->positioned || tstem == stem )
2115     continue;
2116 	allowleft = ( !tstem->ghost || tstem->width == 20 );
2117 	allowright = ( !tstem->ghost || tstem->width == 21 );
2118 	hv = IsUnitHV( &tstem->unit,true );
2119 	corner = (( pd->x_corner && hv == 2 ) || ( pd->y_corner && hv == 1 ));
2120 
2121 	if ( UnitsParallel( &tstem->unit,dir,true ) || tstem->ghost || corner ) {
2122 	    if ( OnStem( tstem,&pd->sp->me,true ) && allowleft ) {
2123 		if ( IsCorrectSide( gd,pd,is_next,true,&tstem->unit )) {
2124 		    AddToStem( gd,tstem,pd,NULL,is_next,false,false );
2125 		    ret++;
2126 		}
2127 	    } else if ( OnStem( tstem,&pd->sp->me,false ) && allowright ) {
2128 		if ( IsCorrectSide( gd,pd,is_next,false,&tstem->unit )) {
2129 		    AddToStem( gd,tstem,NULL,pd,false,is_next,false );
2130 		    ret++;
2131 		}
2132 	    }
2133 	}
2134     }
2135 return( ret );
2136 }
2137 
HalfStem(struct glyphdata * gd,struct pointdata * pd,BasePoint * dir,Spline * other,double other_t,int is_next,int eidx)2138 static struct stemdata *HalfStem( struct glyphdata *gd,struct pointdata *pd,
2139     BasePoint *dir,Spline *other,double other_t,int is_next,int eidx ) {
2140     /* Find the spot on other where the slope is the same as dir */
2141     double t1;
2142     double width;
2143     BasePoint match;
2144     struct stemdata *stem = NULL, *tstem;
2145     struct pointdata *pd2 = NULL, *tpd;
2146     int i;
2147 
2148     t1 = FindSameSlope( other,dir,other_t );
2149     if ( t1==-1e4 )
2150 return( NULL );
2151     if ( t1<0 && other->from->prev!=NULL && gd->points[other->from->ptindex].colinear ) {
2152 	other = other->from->prev;
2153 	t1 = FindSameSlope(other,dir,1.0);
2154     } else if ( t1>1 && other->to->next!=NULL && gd->points[other->to->ptindex].colinear ) {
2155 	other = other->to->next;
2156 	t1 = FindSameSlope(other,dir,0.0);
2157     }
2158 
2159     if ( t1<-.001 || t1>1.001 )
2160 return( NULL );
2161 
2162     /* Ok. the opposite edge has the right slope at t1 */
2163     /* Now see if we can make a one sided stem out of these two */
2164     match.x = ((other->splines[0].a*t1+other->splines[0].b)*t1+other->splines[0].c)*t1+other->splines[0].d;
2165     match.y = ((other->splines[1].a*t1+other->splines[1].b)*t1+other->splines[1].c)*t1+other->splines[1].d;
2166 
2167     width = (match.x-pd->sp->me.x)*dir->y - (match.y-pd->sp->me.y)*dir->x;
2168     /* offset = (match.x-pd->sp->me.x)*dir->x + (match.y-pd->sp->me.y)*dir->y;*/
2169     if ( width<.5 && width>-.5 )
2170 return( NULL );		/* Zero width stems aren't interesting */
2171 
2172     if ( isnan(t1))
2173 	IError( "NaN value in HalfStem" );
2174 
2175     if ( is_next ) {
2176 	pd->nextedges[eidx] = other;
2177 	pd->next_e_t[eidx] = t1;
2178     } else {
2179 	pd->prevedges[eidx] = other;
2180 	pd->prev_e_t[eidx] = t1;
2181     }
2182 
2183     /* In my experience the only case where this function may be useful */
2184     /* is when it occasionally finds a real spline point which for some */
2185     /* reasons has been neglected by other tests and yet forms a valid  */
2186     /* pair for the first point. So run through points and see if we    */
2187     /* have actually got just a position on spline midway between to points, */
2188     /* or it is a normal point allowing to make a normal stem chunk */
2189     for ( i=0; i<gd->pcnt; ++i ) {
2190 	tpd = &gd->points[i];
2191 	if ( tpd->sp != NULL && tpd->sp->me.x == match.x && tpd->sp->me.y == match.y ) {
2192 	    pd2 = tpd;
2193     break;
2194 	}
2195     }
2196     for ( i=0; i<gd->stemcnt; ++i ) {
2197 	tstem = &gd->stems[i];
2198 	if ( UnitsParallel( &tstem->unit,dir,true ) &&
2199 	    BothOnStem( tstem,&pd->base,&match,false,false,false )) {
2200 	    stem = tstem;
2201     break;
2202 	}
2203     }
2204     if ( stem == NULL )
2205 	stem = NewStem(gd,dir,&pd->sp->me,&match);
2206 
2207     AddToStem( gd,stem,pd,pd2,is_next,false,false );
2208 return( stem );
2209 }
2210 
ConnectsAcross(struct glyphdata * gd,SplinePoint * sp,int is_next,Spline * findme,int eidx)2211 static int ConnectsAcross( struct glyphdata *gd,SplinePoint *sp,
2212     int is_next,Spline *findme,int eidx ) {
2213     struct pointdata *pd = &gd->points[sp->ptindex];
2214     Spline *other, *test;
2215     BasePoint dir;
2216 
2217     other = ( is_next ) ? pd->nextedges[eidx] : pd->prevedges[eidx];
2218 
2219     if ( other==findme )
2220 return( true );
2221     if ( other==NULL )
2222 return( false );
2223 
2224     dir.x = ( is_next ) ? -pd->nextunit.x : pd->prevunit.x;
2225     dir.y = ( is_next ) ? -pd->nextunit.y : pd->prevunit.y;
2226     test = other->to->next;
2227     while ( test!=NULL && test != other &&
2228 	    gd->points[test->from->ptindex].nextunit.x * dir.x +
2229 	    gd->points[test->from->ptindex].nextunit.y * dir.y > 0 ) {
2230 	if ( test==findme )
2231 return( true );
2232 	test = test->to->next;
2233     }
2234 
2235     dir.x = ( is_next ) ? pd->nextunit.x : -pd->prevunit.x;
2236     dir.y = ( is_next ) ? pd->nextunit.y : -pd->prevunit.y;
2237     test = other->from->prev;
2238     while ( test!=NULL && test != other &&
2239 	    gd->points[test->to->ptindex].prevunit.x * dir.x +
2240 	    gd->points[test->to->ptindex].prevunit.y * dir.y > 0 ) {
2241 	if ( test==findme )
2242 return( true );
2243 	test = test->from->prev;
2244     }
2245 return( false );
2246 }
2247 
ConnectsAcrossToStem(struct glyphdata * gd,struct pointdata * pd,int is_next,struct stemdata * target,int is_l,int eidx)2248 static int ConnectsAcrossToStem( struct glyphdata *gd,struct pointdata *pd,
2249     int is_next,struct stemdata *target,int is_l,int eidx ) {
2250 
2251     Spline *other, *test;
2252     BasePoint dir;
2253     struct pointdata *tpd;
2254     int ecnt, stemidx;
2255 
2256     ecnt = ( is_next ) ? pd->next_e_cnt : pd->prev_e_cnt;
2257     if ( ecnt < eidx + 1 )
2258 return( false );
2259     other = ( is_next ) ? pd->nextedges[eidx] : pd->prevedges[eidx];
2260 
2261     test = other;
2262     dir.x = ( is_next ) ? pd->nextunit.x : -pd->prevunit.x;
2263     dir.y = ( is_next ) ? pd->nextunit.y : -pd->prevunit.y;
2264     do {
2265 	tpd = &gd->points[test->to->ptindex];
2266 	stemidx = IsStemAssignedToPoint( tpd,target,false );
2267 	if ( stemidx != -1 && tpd->prev_is_l[stemidx] == !is_l &&
2268 	    IsSplinePeak( gd,tpd,rint( target->unit.y ),rint( target->unit.y ),7 ))
2269 return( true );
2270 
2271 	test = test->to->next;
2272     } while ( test!=NULL && test != other && stemidx == -1 &&
2273 	( tpd->prevunit.x * dir.x + tpd->prevunit.y * dir.y >= 0 ));
2274 
2275     test = other;
2276     dir.x = ( is_next ) ? -pd->nextunit.x : pd->prevunit.x;
2277     dir.y = ( is_next ) ? -pd->nextunit.y : pd->prevunit.y;
2278     do {
2279 	tpd = &gd->points[test->from->ptindex];
2280 	stemidx = IsStemAssignedToPoint( tpd,target,true );
2281 	if ( stemidx != -1 && tpd->next_is_l[stemidx] == !is_l &&
2282 	    IsSplinePeak( gd,tpd,rint( target->unit.y ),rint( target->unit.y ),7 ))
2283 return( true );
2284 
2285 	test = test->from->prev;
2286     } while ( test!=NULL && test != other && stemidx == -1 &&
2287 	( tpd->nextunit.x * dir.x + tpd->nextunit.y * dir.y >= 0 ));
2288 return( false );
2289 }
2290 
RecalcT(Spline * base,SplinePoint * from,SplinePoint * to,double curt)2291 static double RecalcT( Spline *base,SplinePoint *from, SplinePoint *to, double curt ) {
2292     double baselen, fromlen, tolen, ret;
2293     Spline *cur;
2294 
2295     baselen = SplineLength( base );
2296     fromlen = baselen * curt;
2297     tolen = baselen * ( 1 - curt );
2298 
2299     cur = base->from->prev;
2300     while ( cur != NULL && cur->to != from ) {
2301 	fromlen += SplineLength( cur );
2302 	cur = cur->from->prev;
2303     }
2304     cur = base->to->next;
2305     while ( cur!= NULL && cur->from != to ) {
2306 	tolen += SplineLength( cur );
2307 	cur = cur->to->next;
2308     }
2309     ret = fromlen/( fromlen + tolen );
2310 return( ret );
2311 }
2312 
BuildStem(struct glyphdata * gd,struct pointdata * pd,int is_next,int require_existing,int has_existing,int eidx)2313 static int BuildStem( struct glyphdata *gd,struct pointdata *pd,int is_next,
2314     int require_existing,int has_existing,int eidx ) {
2315     BasePoint *dir;
2316     Spline *other, *cur;
2317     double t;
2318     double tod, fromd, dist;
2319     SplinePoint *testpt, *topt, *frompt;
2320     struct linedata *line;
2321     struct pointdata *testpd, *topd, *frompd;
2322     int tp, fp, t_needs_recalc=false, ret=0;
2323     uint8 tstub=0, fstub=0;
2324     BasePoint opposite;
2325     struct stemdata *stem=NULL;
2326 
2327     if ( is_next ) {
2328 	dir = &pd->nextunit;
2329 	other = pd->nextedges[eidx];
2330 	cur = pd->sp->next;
2331 	t = pd->next_e_t[eidx];
2332 	dist = pd->next_dist[eidx];
2333     } else {
2334 	dir = &pd->prevunit;
2335 	other = pd->prevedges[eidx];
2336 	cur = pd->sp->prev;
2337 	t = pd->prev_e_t[eidx];
2338 	dist = pd->prev_dist[eidx];
2339     }
2340     topt = other->to; frompt = other->from;
2341     topd = &gd->points[topt->ptindex];
2342     frompd = &gd->points[frompt->ptindex];
2343 
2344     line = is_next ? pd->nextline : pd->prevline;
2345     if ( !IsUnitHV( dir,true ) && line != NULL)
2346 	dir = &line->unit;
2347 
2348     if ( other==NULL )
2349 return( 0 );
2350 
2351     opposite.x = ((other->splines[0].a*t+other->splines[0].b)*t+other->splines[0].c)*t+other->splines[0].d;
2352     opposite.y = ((other->splines[1].a*t+other->splines[1].b)*t+other->splines[1].c)*t+other->splines[1].d;
2353 
2354     if ( eidx == 0 ) tstub = IsStubOrIntersection( gd,dir,pd,topd,is_next,false );
2355     if ( eidx == 0 ) fstub = IsStubOrIntersection( gd,dir,pd,frompd,is_next,true );
2356     tp = ParallelToDir( topd,false,dir,&opposite,pd->sp,tstub );
2357     fp = ParallelToDir( frompd,true,dir,&opposite,pd->sp,fstub );
2358 
2359     /* if none of the opposite points is parallel to the needed vector, then    */
2360     /* give it one more chance by skipping those points and looking at the next */
2361     /* and previous one. This can be useful in situations where the opposite    */
2362     /* edge cannot be correctly detected just because there are too many points */
2363     /* on the spline (which is a very common situation for poorly designed      */
2364     /* fonts or fonts with quadratic splines). */
2365     /* But do that only for colinear spline segments and ensure that there are  */
2366     /* no bends between two splines. */
2367     if ( !tp && ( !fp || t > 0.5 ) &&
2368 	topd->colinear && &other->to->next != NULL ) {
2369 	testpt = topt->next->to;
2370 	testpd = &gd->points[testpt->ptindex];
2371 	BasePoint *initdir = &topd->prevunit;
2372 	while ( !tp && topd->colinear && pd->sp != testpt && other->from != testpt && (
2373 	    testpd->prevunit.x * initdir->x +
2374 	    testpd->prevunit.y * initdir->y > 0 )) {
2375 
2376 	    topt = testpt; topd = testpd;
2377 	    tp = ParallelToDir( topd,false,dir,&opposite,pd->sp,false );
2378 	    testpt = topt->next->to;
2379 	    testpd = &gd->points[testpt->ptindex];
2380 	}
2381 	if ( tp ) t_needs_recalc = true;
2382     }
2383     if ( !fp && ( !fp || t < 0.5 ) &&
2384 	frompd->colinear && &other->from->prev != NULL ) {
2385 	testpt = frompt->prev->from;
2386 	testpd = &gd->points[testpt->ptindex];
2387 	BasePoint *initdir = &frompd->prevunit;
2388 	while ( !fp && frompd->colinear && pd->sp != testpt && other->to != testpt && (
2389 	    testpd->prevunit.x * initdir->x +
2390 	    testpd->prevunit.y * initdir->y > 0 )) {
2391 
2392 	    frompt = testpt; frompd = testpd;
2393 	    fp = ParallelToDir( frompd,true,dir,&opposite,pd->sp,false );
2394 	    testpt = frompt->prev->from;
2395 	    testpd = &gd->points[testpt->ptindex];
2396 	}
2397 	if ( fp ) t_needs_recalc = true;
2398     }
2399     if ( t_needs_recalc )
2400 	t = RecalcT( other,frompt,topt,t );
2401     if ( !tp && !fp ) {
2402 	if ( has_existing )
2403 	    ret = HalfStemNoOpposite( gd,pd,NULL,dir,is_next );
2404 return( ret );
2405     }
2406 
2407     /* We have several conflicting metrics for getting the "better" stem */
2408     /* Generally we prefer the stem with the smaller width (but not always. See tilde) */
2409     /* Generally we prefer the stem formed by the point closer to the intersection */
2410     tod = (1-t)*NormalDist( &topt->me,&pd->sp->me,dir );
2411     fromd = t*NormalDist( &frompt->me,&pd->sp->me,dir );
2412 
2413     if ( tp && (( tod<fromd ) ||
2414 	( !fp && ( tod<2*fromd || dist < topd->prev_dist[eidx] ||
2415 	    ConnectsAcross( gd,frompt,true,cur,eidx ) || NearlyParallel( dir,other,t ))))) {
2416 	stem = TestStem( gd,pd,dir,topt,is_next,false,require_existing,tstub,eidx );
2417     }
2418     if ( stem == NULL && fp && (( fromd<tod ) ||
2419 	( !tp && ( fromd<2*tod || dist < frompd->next_dist[eidx] ||
2420 	    ConnectsAcross( gd,topt,false,cur,eidx ) || NearlyParallel( dir,other,t ))))) {
2421 	stem = TestStem( gd,pd,dir,frompt,is_next,true,require_existing,fstub,eidx );
2422     }
2423     if ( eidx == 0 && stem == NULL && !require_existing && cur!=NULL &&
2424 	!other->knownlinear && !cur->knownlinear )
2425 	stem = HalfStem( gd,pd,dir,other,t,is_next,eidx );
2426     if ( stem != NULL ) ret = 1;
2427     if ( has_existing )
2428 	ret += HalfStemNoOpposite( gd,pd,stem,dir,is_next );
2429 return( ret );
2430 }
2431 
AssignLinePointsToStems(struct glyphdata * gd)2432 static void AssignLinePointsToStems( struct glyphdata *gd ) {
2433     struct pointdata *pd;
2434     struct stemdata *stem;
2435     struct linedata *line;
2436     struct stem_chunk *chunk;
2437     int i, j, stem_hv, line_hv, needs_hv=false;
2438 
2439     for ( i=0; i<gd->stemcnt; ++i ) if ( !gd->stems[i].toobig ) {
2440 	stem = &gd->stems[i];
2441 	stem_hv = IsUnitHV( &stem->unit,true );
2442 	needs_hv = ( stem_hv || ( stem->chunk_cnt == 1 &&
2443 	    stem->chunks[0].stub && IsUnitHV( &stem->unit,false )));
2444 
2445 	if ( stem->leftline != NULL ) {
2446 	    line = stem->leftline;
2447 	    line_hv = ( needs_hv && LineFitsHV( line ));
2448 
2449 	    if ( needs_hv && !line_hv )
2450 		stem->leftline = NULL;
2451 	    else {
2452 		for ( j=0; j<line->pcnt; j++ ) {
2453 		    pd = line->points[j];
2454 		    if ( pd->prevline == line && OnStem( stem,&pd->base,true ) &&
2455 			IsStemAssignedToPoint( pd,stem,false ) == -1) {
2456 			chunk = AddToStem( gd,stem,pd,NULL,false,false,false );
2457 			chunk->lpotential = true;
2458 		    } if ( pd->nextline == line && OnStem( stem,&pd->base,true ) &&
2459 			IsStemAssignedToPoint( pd,stem,true ) == -1 ) {
2460 			chunk = AddToStem( gd,stem,pd,NULL,true,false,false );
2461 			chunk->lpotential = true;
2462 		    }
2463 		}
2464 	    }
2465 	}
2466 	if ( stem->rightline != NULL ) {
2467 	    line = stem->rightline;
2468 	    line_hv = ( needs_hv && LineFitsHV( line ));
2469 
2470 	    if ( needs_hv && !line_hv )
2471 		stem->rightline = NULL;
2472 	    else {
2473 		for ( j=0; j<line->pcnt; j++ ) {
2474 		    pd = line->points[j];
2475 		    if ( pd->prevline == line && OnStem( stem,&pd->base,false ) &&
2476 			IsStemAssignedToPoint( pd,stem,false ) == -1 ) {
2477 			chunk = AddToStem( gd,stem,NULL,pd,false,false,false );
2478 			chunk->rpotential = true;
2479 		    } if ( pd->nextline == line && OnStem( stem,&pd->base,false ) &&
2480 			IsStemAssignedToPoint( pd,stem,true ) == -1 ) {
2481 			chunk = AddToStem( gd,stem,NULL,pd,false,true,false );
2482 			chunk->rpotential = true;
2483 		    }
2484 		}
2485 	    }
2486 	}
2487     }
2488 }
2489 
DiagonalCornerStem(struct glyphdata * gd,struct pointdata * pd,int require_existing)2490 static struct stemdata *DiagonalCornerStem( struct glyphdata *gd,
2491     struct pointdata *pd,int require_existing ) {
2492     Spline *other = pd->bothedge;
2493     struct pointdata *pfrom = NULL, *pto = NULL, *pd2 = NULL, *pd3=NULL;
2494     double width, length;
2495     struct stemdata *stem;
2496     struct stem_chunk *chunk;
2497 
2498     pfrom = &gd->points[other->from->ptindex];
2499     pto = &gd->points[other->to->ptindex];
2500     if ( pd->symetrical_h && pto->symetrical_h && pd->both_e_t>.9 )
2501 	pd2 = pto;
2502     else if ( pd->symetrical_h && pfrom->symetrical_h && pd->both_e_t<.1 )
2503 	pd2 = pfrom;
2504     else if ( pd->symetrical_v && pto->symetrical_v && pd->both_e_t>.9 )
2505 	pd2 = pto;
2506     else if ( pd->symetrical_v && pfrom->symetrical_v && pd->both_e_t<.1 )
2507 	pd2 = pfrom;
2508     else if ( pd->symetrical_h && other->islinear && other->splines[1].c==0 ) {
2509 	pd2 = pfrom;
2510 	pd3 = pto;
2511     } else if ( pd->symetrical_v && other->islinear && other->splines[0].c==0 ) {
2512 	pd2 = pfrom;
2513 	pd3 = pto;
2514     } else
2515 return( NULL );
2516 
2517     if ( pd->symetrical_v )
2518 	width = (pd->sp->me.x-pd2->sp->me.x);
2519     else
2520 	width = (pd->sp->me.y-pd2->sp->me.y);
2521     length = (pd->sp->next->to->me.x-pd->sp->me.x)*(pd->sp->next->to->me.x-pd->sp->me.x) +
2522 	     (pd->sp->next->to->me.y-pd->sp->me.y)*(pd->sp->next->to->me.y-pd->sp->me.y);
2523     if ( width*width>length )
2524 return( NULL );
2525 
2526     stem = FindOrMakeHVStem(gd,pd,pd2,pd->symetrical_h,require_existing);
2527 
2528     if ( pd3 == NULL && stem != NULL )
2529 	chunk = AddToStem( gd,stem,pd,pd2,2,2,2 );
2530     else if ( stem != NULL ) {
2531 	chunk = AddToStem( gd,stem,pd,pd2,2,2,3 );
2532 	chunk = AddToStem( gd,stem,pd,pd3,2,2,3 );
2533     }
2534 return( stem );
2535 }
2536 
chunk_cmp(const void * _p1,const void * _p2)2537 static int chunk_cmp( const void *_p1, const void *_p2 ) {
2538     const struct stem_chunk *ch1 = _p1, *ch2 = _p2;
2539 
2540     struct stemdata *stem;
2541     double loff1=0,roff1=0,loff2=0,roff2=0;
2542 
2543     stem = ch1->parent;
2544     if ( stem==NULL )
2545 return( 0 );
2546 
2547     if ( ch1->l != NULL )
2548 	loff1 = ( ch1->l->sp->me.x - stem->left.x ) * stem->unit.x +
2549 		( ch1->l->sp->me.y - stem->left.y ) * stem->unit.y;
2550     if ( ch1->r != NULL )
2551 	roff1 = ( ch1->r->sp->me.x - stem->right.x ) * stem->unit.x +
2552 		( ch1->r->sp->me.y - stem->right.y ) * stem->unit.y;
2553     if ( ch2->l != NULL )
2554 	loff2 = ( ch2->l->sp->me.x - stem->left.x ) * stem->unit.x +
2555 		( ch2->l->sp->me.y - stem->left.y ) * stem->unit.y;
2556     if ( ch2->r != NULL )
2557 	roff2 = ( ch2->r->sp->me.x - stem->right.x ) * stem->unit.x +
2558 		( ch2->r->sp->me.y - stem->right.y ) * stem->unit.y;
2559 
2560     if ( loff1>loff2 )
2561 return( 1 );
2562     else if ( loff1<loff2 )
2563 return( -1 );
2564     else {
2565 	if ( roff1>roff2 )
2566 return( 1 );
2567 	else if ( roff1<roff2 )
2568 return( -1 );
2569 	else
2570 return( 0 );
2571     }
2572 }
2573 
stem_cmp(const void * _p1,const void * _p2)2574 static int stem_cmp( const void *_p1, const void *_p2 ) {
2575     struct stemdata * const *st1 = _p1, * const *st2 = _p2;
2576     double start1, end1, start2, end2;
2577 
2578     if ( fabs( (*st1)->unit.x ) > fabs( (*st1)->unit.y )) {
2579 	start1 = (*st1)->right.y; end1 = (*st1)->left.y;
2580 	start2 = (*st2)->right.y; end2 = (*st2)->left.y;
2581     } else {
2582 	start1 = (*st1)->left.x; end1 = (*st1)->right.x;
2583 	start2 = (*st2)->left.x; end2 = (*st2)->right.x;
2584     }
2585 
2586     if ( start1 > start2 )
2587 return( 1 );
2588     else if ( start1 < start2 )
2589 return( -1 );
2590     else {
2591 	if ( end1 > end2 )
2592 return( 1 );
2593 	else if ( end1 < end2 )
2594 return( -1 );
2595 	else
2596 return( 0 );
2597     }
2598 }
2599 
FixupT(struct pointdata * pd,int stemidx,int isnext,int eidx)2600 static void FixupT( struct pointdata *pd,int stemidx,int isnext, int eidx ) {
2601     /* When we calculated "next/prev_e_t" we deliberately did not use pd1->me */
2602     /*  (because things get hard at intersections) so our t is only an approx-*/
2603     /*  imation. We can do a lot better now */
2604     Spline *s;
2605     Spline myline;
2606     SplinePoint end1, end2;
2607     double width,t,sign, len, dot;
2608     BasePoint pts[9];
2609     extended lts[10], sts[10];
2610     BasePoint diff;
2611     struct stemdata *stem ;
2612 
2613     if ( pd == NULL || stemidx == -1 )
2614 return;
2615     stem = ( isnext ) ? pd->nextstems[stemidx] : pd->prevstems[stemidx];
2616     width = ( stem->right.x - stem->left.x )*stem->unit.y -
2617 	    ( stem->right.y-stem->left.y )*stem->unit.x;
2618     s = ( isnext ) ? pd->nextedges[eidx] : pd->prevedges[eidx];
2619     if ( s==NULL )
2620 return;
2621     diff.x = s->to->me.x-s->from->me.x;
2622     diff.y = s->to->me.y-s->from->me.y;
2623     if ( diff.x<.001 && diff.x>-.001 && diff.y<.001 && diff.y>-.001 )
2624 return;		/* Zero length splines give us NaNs */
2625     len = sqrt( pow( diff.x,2 ) + pow( diff.y,2 ));
2626     dot = ( diff.x*stem->unit.x + diff.y*stem->unit.y )/len;
2627     if ( dot < .0004 && dot > -.0004 )
2628 return;		/* It's orthogonal to our stem */
2629 
2630     if (( stem->unit.x==1 || stem->unit.x==-1 ) && s->knownlinear )
2631 	t = (pd->sp->me.x-s->from->me.x)/(s->to->me.x-s->from->me.x);
2632     else if (( stem->unit.y==1 || stem->unit.y==-1 ) && s->knownlinear )
2633 	t = (pd->sp->me.y-s->from->me.y)/(s->to->me.y-s->from->me.y);
2634     else {
2635 	memset(&myline,0,sizeof(myline));
2636 	memset(&end1,0,sizeof(end1));
2637 	memset(&end2,0,sizeof(end2));
2638 	sign = (( isnext && pd->next_is_l[stemidx] ) || ( !isnext && pd->prev_is_l[stemidx] )) ? 1 : -1;
2639 	myline.knownlinear = myline.islinear = true;
2640 	end1.me = pd->sp->me;
2641 	end2.me.x = pd->sp->me.x+1.1*sign*width*stem->l_to_r.x;
2642 	end2.me.y = pd->sp->me.y+1.1*sign*width*stem->l_to_r.y;
2643 	end1.nextcp = end1.prevcp = end1.me;
2644 	end2.nextcp = end2.prevcp = end2.me;
2645 	end1.nonextcp = end1.noprevcp = end2.nonextcp = end2.noprevcp = true;
2646 	end1.next = &myline; end2.prev = &myline;
2647 	myline.from = &end1; myline.to = &end2;
2648 	myline.splines[0].d = end1.me.x;
2649 	myline.splines[0].c = end2.me.x-end1.me.x;
2650 	myline.splines[1].d = end1.me.y;
2651 	myline.splines[1].c = end2.me.y-end1.me.y;
2652 	if ( SplinesIntersect(&myline,s,pts,lts,sts)<=0 )
2653 return;
2654 	t = sts[0];
2655     }
2656     if ( isnan(t))
2657 	IError( "NaN value in FixupT" );
2658     if ( isnext )
2659 	pd->next_e_t[eidx] = t;
2660     else
2661 	pd->prev_e_t[eidx] = t;
2662 }
2663 
2664 /* flags: 1 -- accept curved extrema, 2 -- accept angles, */
2665 /*	4 -- analyze segments (not just single points)    */
IsSplinePeak(struct glyphdata * gd,struct pointdata * pd,int outer,int is_x,int flags)2666 static int IsSplinePeak( struct glyphdata *gd,struct pointdata *pd,
2667     int outer,int is_x,int flags ) {
2668 
2669     double base, next, prev, nextctl, prevctl, unit_p, unit_n;
2670     Spline *s, *snext, *sprev;
2671     struct monotonic **space, *m;
2672     int wprev, wnext, i, desired;
2673     SplinePoint *sp = pd->sp;
2674 
2675     base = ((real *) &sp->me.x)[!is_x];
2676     nextctl = sp->nonextcp ? base : ((real *) &sp->nextcp.x)[!is_x];
2677     prevctl = sp->noprevcp ? base : ((real *) &sp->prevcp.x)[!is_x];
2678     next = prev = base;
2679     snext = sp->next; sprev = sp->prev;
2680 
2681     if ( snext->to == NULL || sprev->from == NULL )
2682 return( false );
2683     if (!( flags & 2) && ( sp->nonextcp || sp->noprevcp ))
2684 return( false );
2685     else if (!( flags & 1 ) && ( pd->colinear ))
2686 return( false );
2687 
2688     if ( flags & 4 ) {
2689 	while ( snext->to->next != NULL && snext->to != sp && next == base ) {
2690 	    next = ((real *) &snext->to->me.x)[!is_x];
2691 	    snext = snext->to->next;
2692 	}
2693 
2694 	while ( sprev->from->prev != NULL && sprev->from != sp && prev == base ) {
2695 	    prev = ((real *) &sprev->from->me.x)[!is_x];
2696 	    sprev = sprev->from->prev;
2697 	}
2698     } else {
2699 	next = ((real *) &snext->to->me.x)[!is_x];
2700 	prev = ((real *) &sprev->from->me.x)[!is_x];
2701     }
2702 
2703     if ( prev<base && next<base && nextctl<=base && prevctl<=base )
2704 	desired = ( outer ) ? -1 : 1;
2705     else if ( prev>base && next>base && prevctl>=base && nextctl>=base )
2706 	desired = ( outer ) ? 1 : -1;
2707     else
2708 return( false );
2709 
2710     MonotonicFindAt( gd->ms,is_x,((real *) &sp->me.x)[is_x],space = gd->space );
2711     wprev = wnext = 0;
2712     for ( i=0; space[i]!=NULL; ++i ) {
2713 	m = space[i];
2714 	s = m->s;
2715 
2716 	if ( s->from == sp )
2717 	    wnext = ((&m->xup)[is_x] ? 1 : -1 );
2718 	else if ( s->to == sp )
2719 	    wprev = ((&m->xup)[is_x] ? 1 : -1 );
2720     }
2721 
2722     if ( wnext != 0 && wprev != 0 && wnext != wprev ) {
2723 	unit_p = (&pd->prevunit.x)[!is_x];
2724 	unit_n = (&pd->nextunit.x)[!is_x];
2725 	if ( unit_p < unit_n && (
2726 	    ( outer && wprev == 1 ) || ( !outer && wprev == -1 )))
2727 return( desired );
2728 	else if ( unit_p > unit_n && (
2729 	    ( outer && wnext == 1 ) || ( !outer && wnext == -1 )))
2730 return( desired );
2731     } else {
2732 	if ( wnext == desired || wprev == desired )
2733 return( desired );
2734     }
2735 
2736 return( false );
2737 }
2738 
FindClosestOpposite(struct stemdata * stem,struct stem_chunk ** chunk,SplinePoint * sp,int * next)2739 static struct pointdata *FindClosestOpposite(
2740     struct stemdata *stem,struct stem_chunk **chunk,SplinePoint *sp,int *next ) {
2741 
2742     struct pointdata *pd, *ret=NULL;
2743     struct stem_chunk *testchunk;
2744     double test, proj=1e4;
2745     int i, is_l;
2746 
2747     for ( i=0; i<stem->chunk_cnt; ++i ) {
2748 	testchunk = &stem->chunks[i];
2749 	pd = NULL;
2750 	if ( testchunk->l != NULL && testchunk->l->sp==sp ) {
2751 	    pd = testchunk->r;
2752 	    is_l = false;
2753 	} else if ( testchunk->r != NULL && testchunk->r->sp==sp ) {
2754 	    pd = testchunk->l;
2755 	    is_l = true;
2756 	}
2757 
2758 	if ( pd != NULL ) {
2759 	    test = ( pd->sp->me.x-sp->me.x ) * stem->unit.x +
2760 		   ( pd->sp->me.y-sp->me.y ) * stem->unit.y;
2761 	    if ( test < 0 ) test = -test;
2762 	    if ( test < proj ) {
2763 		ret = pd;
2764 		proj = test;
2765 		*chunk = testchunk;
2766 	    }
2767 	}
2768     }
2769     if ( ret != NULL )
2770 	*next = ( is_l ) ? (*chunk)->lnext : (*chunk)->rnext;
2771 return( ret );
2772 }
2773 
ValueChunk(struct glyphdata * gd,struct vchunk * vchunks,int chcnt,int idx,int l_base)2774 static int ValueChunk( struct glyphdata *gd,struct vchunk *vchunks,
2775     int chcnt,int idx,int l_base ) {
2776 
2777     int peak1=0, peak2=0, val=0;
2778     int i, is_x, base_next, opp_next;
2779     struct pointdata *base, *opp, *frompd, *topd;
2780     struct stem_chunk *chunk = vchunks[idx].chunk, *tchunk;
2781     struct stemdata *stem = chunk->parent;
2782     double norm, dist;
2783     Spline *sbase, *sopp, *other;
2784 
2785     /* If a stem was already present before generating glyph data, */
2786     /* then it should always be preferred in case of a conflict    */
2787     if ( stem->positioned || chunk->stemcheat ) val++;
2788 
2789     if ( l_base ) {
2790 	base = chunk->l; opp = chunk->r;
2791 	base_next = chunk->lnext; opp_next = chunk->rnext;
2792     } else {
2793 	base = chunk->r; opp = chunk->l;
2794 	base_next = chunk->rnext; opp_next = chunk->lnext;
2795     }
2796     sbase = ( base_next ) ? base->sp->next : base->sp->prev;
2797     sopp = ( opp_next ) ? opp->sp->next : opp->sp->prev;
2798     other = ( opp_next ) ? opp->nextedges[0] : opp->prevedges[0];
2799 
2800     /* If there are 2 conflicting chunks belonging to different stems but       */
2801     /* based on the same point, then we have to decide which stem is "better"   */
2802     /* for that point. We compare stems (or rather chunks) by assigning a       */
2803     /* value to each of them and then prefer the stem whose value is positive.  */
2804     /* A chunk gets a +1 value bonus in the following cases:                    */
2805     /* - The stem is vertical/horizontal and splines are curved in the same     */
2806     /*   direction at both sides of the chunk;                                  */
2807     /* - A stem has both its width and the distance between the opposite points */
2808     /*   smaller than another stem;                                             */
2809     /* - The common side of two stems is a straight line formed by two points   */
2810     /*   and the opposite point can be projected to line segment between those  */
2811     /*   two points. */
2812     if ( IsUnitHV( &stem->unit,true ) && !sbase->knownlinear ) {
2813 	is_x = (int) rint( stem->unit.y );
2814 	peak1 = ( is_x ) ? base->x_extr : base->y_extr;
2815 	peak2 = ( is_x ) ? opp->x_extr  : opp->y_extr;
2816 
2817 	dist =  ( base->base.x - opp->base.x )*stem->unit.x +
2818 		( base->base.y - opp->base.y )*stem->unit.y;
2819 
2820 	/* Are there any stems attached to the same base point which     */
2821 	/* are narrower than the distance between two points forming the */
2822 	/* given chunk? */
2823 	for ( i=0; i<chcnt; i++ ) {
2824 	    tchunk = vchunks[i].chunk;
2825 	    if ( tchunk == NULL || tchunk == chunk || chunk->l == NULL || chunk->r == NULL )
2826 	continue;
2827 	    norm = tchunk->parent->width;
2828 	    if ( norm < fabs( dist ))
2829 	break;
2830 	}
2831 
2832 	/* If both points are curved in the same direction, then check also    */
2833 	/* the "line of sight" between those points (if there are interventing */
2834 	/* splines, then it is not a real feature bend)*/
2835 	if ( i == chcnt && peak1 + peak2 == 3 && ConnectsAcross( gd,base->sp,opp_next,sopp,0 ))
2836 	    val++;
2837     }
2838 
2839     frompd = &gd->points[sbase->from->ptindex];
2840     topd = &gd->points[sbase->to->ptindex];
2841 
2842     if (IsStemAssignedToPoint( frompd,stem,true ) != -1 &&
2843 	IsStemAssignedToPoint( topd,stem,false ) != -1 )
2844 	if ( other == sbase ) val++;
2845 
2846     dist = vchunks[idx].dist;
2847     for ( i=0; i<chcnt; i++ ) {
2848 	tchunk = vchunks[i].chunk;
2849 	if ( tchunk == NULL || tchunk == chunk ||
2850 	    ( vchunks[idx].parallel && !vchunks[i].parallel ))
2851     continue;
2852 	if ( vchunks[i].dist <= dist || tchunk->parent->width <= stem->width )
2853     break;
2854     }
2855     if ( i==chcnt ) val++;
2856 
2857     /* If just one of the checked chunks has both its sides parallel            */
2858     /* to the stem direction, then we consider it is always worth to be output. */
2859     /* This check was introduced to avoid situations where a stem marking       */
2860     /* a feature termination can be preferred to another stem which controls    */
2861     /* the main part of the same feature */
2862     if ( vchunks[idx].parallel ) {
2863 	for ( i=0; i<chcnt; i++ ) {
2864 	    if ( vchunks[i].chunk == NULL || vchunks[i].chunk == chunk )
2865 	continue;
2866 	    if ( vchunks[i].parallel )
2867 	break;
2868 	}
2869 	if ( i == chcnt ) val++;
2870     }
2871 
2872 return( val );
2873 }
2874 
CheckPotential(struct glyphdata * gd,struct pointdata * pd,int is_next)2875 static void CheckPotential( struct glyphdata *gd,struct pointdata *pd,int is_next ) {
2876     int i, j, is_l, next1, stemcnt, val;
2877     int val_cnt=0;
2878     BasePoint *lunit, *runit;
2879     struct stemdata **stems;
2880     struct vchunk *vchunks;
2881     struct stem_chunk *cur;
2882 
2883     stemcnt = ( is_next ) ? pd->nextcnt : pd->prevcnt;
2884     stems = ( is_next ) ? pd->nextstems : pd->prevstems;
2885     vchunks = calloc( stemcnt,sizeof( VChunk ));
2886 
2887     for ( i=0; i<stemcnt; i++ ) {
2888 	is_l = ( is_next ) ? pd->next_is_l[i] : pd->prev_is_l[i];
2889 	FindClosestOpposite( stems[i],&vchunks[i].chunk,pd->sp,&next1 );
2890 	if ( vchunks[i].chunk == NULL )
2891     continue;
2892 	cur = vchunks[i].chunk;
2893 	if ( vchunks[i].value > 0 ) val_cnt++;
2894 	vchunks[i].dist  =  pow( cur->l->base.x - cur->r->base.x,2 ) +
2895 			    pow( cur->l->base.y - cur->r->base.y,2 );
2896 	lunit = ( cur->lnext ) ? &cur->l->nextunit : &cur->l->prevunit;
2897 	runit = ( cur->rnext ) ? &cur->r->nextunit : &cur->r->prevunit;
2898 	vchunks[i].parallel =   UnitsParallel( lunit,&stems[i]->unit,2 ) &&
2899 				UnitsParallel( runit,&stems[i]->unit,2 );
2900     }
2901 
2902     for ( i=0; i<stemcnt; i++ ) if ( vchunks[i].chunk != NULL ) {
2903 	vchunks[i].value = ValueChunk( gd,vchunks,stemcnt,i,is_l );
2904 	if ( vchunks[i].value ) val_cnt++;
2905     }
2906 
2907     /* If we was unable to figure out any reasons for which at least */
2908     /* one of the checked chunks should really be output, then keep  */
2909     /* all the 'potential' flags as they are and do nothing */
2910     if ( val_cnt > 0 ) {
2911 	for ( i=0; i<stemcnt; i++ ) if ( vchunks[i].chunk != NULL )  {
2912 	    is_l = ( is_next ) ? pd->next_is_l[i] : pd->prev_is_l[i];
2913 	    val = vchunks[i].value;
2914 	    for ( j=0; j<stems[i]->chunk_cnt; j++ ) {
2915 		cur = &stems[i]->chunks[j];
2916 		if ( is_l && cur->l == pd ) {
2917 		    if ( val > 0 ) cur->lpotential = false;
2918 		    else cur->lpotential = true;
2919 		} else if ( !is_l && cur->r == pd ) {
2920 		    if ( val > 0 ) cur->rpotential = false;
2921 		    else cur->rpotential = true;
2922 		}
2923 	    }
2924 	}
2925     }
2926     free( vchunks );
2927 }
2928 
StemIsActiveAt(struct glyphdata * gd,struct stemdata * stem,double stempos)2929 static int StemIsActiveAt( struct glyphdata *gd,struct stemdata *stem,double stempos ) {
2930     BasePoint pos,cpos,mpos;
2931     int which;
2932     double test;
2933     double lmin, lmax, rmin, rmax, loff, roff, minoff, maxoff;
2934     struct monotonic **space, *m;
2935     int winding, nw, closest, i, j;
2936 
2937     pos.x = stem->left.x + stempos*stem->unit.x;
2938     pos.y = stem->left.y + stempos*stem->unit.y;
2939 
2940     if ( IsUnitHV( &stem->unit,true )) {
2941 	which = stem->unit.x==0;
2942 	MonotonicFindAt(gd->ms,which,((real *) &pos.x)[which],space = gd->space);
2943 	test = ((real *) &pos.x)[!which];
2944 
2945 	lmin = ( stem->lmax - 2*dist_error_hv < -dist_error_hv ) ?
2946 	    stem->lmax - 2*dist_error_hv : -dist_error_hv;
2947 	lmax = ( stem->lmin + 2*dist_error_hv > dist_error_hv ) ?
2948 	    stem->lmin + 2*dist_error_hv : dist_error_hv;
2949 	rmin = ( stem->rmax - 2*dist_error_hv < -dist_error_hv ) ?
2950 	    stem->rmax - 2*dist_error_hv : -dist_error_hv;
2951 	rmax = ( stem->rmin + 2*dist_error_hv > dist_error_hv ) ?
2952 	    stem->rmin + 2*dist_error_hv : dist_error_hv;
2953 	minoff = test + ( lmin * stem->unit.y - lmax * stem->unit.x );
2954 	maxoff = test + ( lmax * stem->unit.y - lmin * stem->unit.x );
2955 
2956 	winding = 0; closest = -1;
2957 	for ( i=0; space[i]!=NULL; ++i ) {
2958 	    m = space[i];
2959 	    nw = ((&m->xup)[which] ? 1 : -1 );
2960 	    if ( m->other >= minoff && m->other <= maxoff && nw == 1 ) {
2961 		closest = i;
2962 	break;
2963 	    } else if ( m->other > maxoff )
2964 	break;
2965 	    winding += nw;
2966 	}
2967 	if ( closest < 0 )
2968 return( false );
2969 
2970 	cpos.x = ( which ) ? m->other : pos.x ;
2971 	cpos.y = ( which ) ? pos.y : m->other ;
2972 	loff = ( cpos.x - stem->left.x ) * stem->unit.y -
2973 	       ( cpos.y - stem->left.y ) * stem->unit.x;
2974 	if ( loff > lmax || loff < lmin )
2975 return( false );
2976 
2977 	j = MatchWinding(space,i,nw,winding,which,0);
2978 	if ( j==-1 )
2979 return( false );
2980 	m = space[j];
2981 
2982 	mpos.x = ( which ) ? m->other : pos.x ;
2983 	mpos.y = ( which ) ? pos.y : m->other ;
2984 	roff = ( mpos.x - stem->right.x ) * stem->unit.y -
2985 	       ( mpos.y - stem->right.y ) * stem->unit.x;
2986 	if ( roff >= rmin && roff <= rmax )
2987 return( true );
2988 return( false );
2989     } else {
2990 return( StillStem( gd,dist_error_diag,&pos,stem ));
2991     }
2992 }
2993 
2994 /* This function is used to check the distance between a hint's edge */
2995 /* and a spline and determine the extet where this hint can be */
2996 /* considered "active". */
WalkSpline(struct glyphdata * gd,struct pointdata * pd,int gonext,struct stemdata * stem,int is_l,int force_ac,BasePoint * res)2997 static int WalkSpline( struct glyphdata *gd, struct pointdata *pd,int gonext,
2998     struct stemdata *stem,int is_l,int force_ac,BasePoint *res ) {
2999 
3000     int i, curved;
3001     double off, dist, min, max;
3002     double incr, err;
3003     double t, ratio, width;
3004     Spline *s;
3005     BasePoint *base, *nunit, pos, good;
3006     SplinePoint *sp, *nsp;
3007     struct pointdata *npd;
3008 
3009     err = ( IsUnitHV( &stem->unit,true )) ? dist_error_hv : dist_error_diag;
3010     width = stem->width;
3011     ratio = gd->emsize/( 6 * width );
3012     if ( err > width/2) err = width/2;
3013 
3014     sp = pd->sp;
3015     base = ( is_l ) ? &stem->left : &stem->right;
3016     min = ( is_l ) ? stem->lmax - 2*err : stem->rmax - 2*err;
3017     max = ( is_l ) ? stem->lmin + 2*err : stem->rmin + 2*err;
3018 
3019     s = ( gonext ) ? sp->next : sp->prev;
3020     nsp = ( gonext ) ? s->to : s->from;
3021     npd   = &gd->points[nsp->ptindex];
3022     nunit = ( gonext ) ? &npd->prevunit : &npd->nextunit;
3023     good = sp->me;
3024 
3025     off   = ( nsp->me.x - base->x )*stem->l_to_r.x +
3026 	    ( nsp->me.y - base->y )*stem->l_to_r.y;
3027     /* Some splines have tiny control points and are almost flat */
3028     /*  think of them as lines then rather than treating them as curves */
3029     /*  figure out how long they remain within a few orthoganal units of */
3030     /*  the point */
3031     /* We used to check the distance between a control point and a spline */
3032     /* and consider the segment "flat" if this distance is smaller than   */
3033     /* the normal allowed "error" value. However this method doesn't produce */
3034     /* consistent results if the spline is not long enough (as usual for  */
3035     /* fonts with quadratic splines). So now we consider a spline "flat"  */
3036     /* only if it never deviates too far from the hint's edge and both    */
3037     /* its terminal points are snappable to the same hint */
3038     curved = ( IsStemAssignedToPoint( npd,stem,gonext ) == -1 &&
3039 	( off < min || off > max || !UnitsParallel( &stem->unit,nunit,true )));
3040 
3041     /* If a spline does deviate from the edge too far to consider it flat, */
3042     /* then we calculate the extent where the spline and the edge are still */
3043     /* close enough to consider the hint active at this zone. If the hint is */
3044     /* still active at the end of the spline, we can check some subsequent splines */
3045     /* too. This method produces better effect than any "magic" manipulations */
3046     /* with control point coordinates, because it takes into account just the */
3047     /* spline configuration rather than point positions */
3048     if ( curved ) {
3049 	max = err = dist_error_curve;
3050 	min = -dist_error_curve;
3051 	/* The following statement forces our code to detect an active zone */
3052 	/* even if all checks actually fail. This makes sense for stems */
3053 	/* marking arks and bends */
3054 	if ( force_ac )
3055 	    good = ( gonext ) ? sp->nextcp : sp->prevcp;
3056 	/* If a spline is closer to the opposite stem edge than to the current edge, then we */
3057 	/* can no longer consider the stem active at this point */
3058 	if ( err > width/2 ) err = width/2;
3059 
3060 	t = ( gonext ) ? 0.9999 : 0.0001;
3061 	for ( ; ; s = ( gonext ) ? s->to->next : s->from->prev ) {
3062 	    pos.x = ((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d;
3063 	    pos.y = ((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d;
3064 	    off   = ( pos.x - base->x )*stem->l_to_r.x +
3065 		    ( pos.y - base->y )*stem->l_to_r.y;
3066 	    dist  = ( pos.x - sp->me.x )*stem->unit.x +
3067 		    ( pos.y - sp->me.y )*stem->unit.y;
3068 	    nsp   = ( gonext ) ? s->to : s->from;
3069 	    npd   = &gd->points[nsp->ptindex];
3070 	    if (fabs( off ) < max && fabs( dist ) <= ( width + width * ratio ) &&
3071 		nsp != sp && npd->colinear && !npd->x_extr && !npd->y_extr &&
3072 		StillStem( gd,err,&pos,stem ))
3073 		good = pos;
3074 	    else
3075 	break;
3076 	}
3077     }
3078     t = .5;
3079     incr = ( gonext ) ? .25 : -.25;
3080     for ( i=0; i<6; ++i ) {
3081 	pos.x = ((s->splines[0].a*t+s->splines[0].b)*t+s->splines[0].c)*t+s->splines[0].d;
3082 	pos.y = ((s->splines[1].a*t+s->splines[1].b)*t+s->splines[1].c)*t+s->splines[1].d;
3083 	off   = ( pos.x - base->x )*stem->l_to_r.x +
3084 		( pos.y - base->y )*stem->l_to_r.y;
3085 	dist  = ( pos.x - sp->me.x )*stem->unit.x +
3086 		( pos.y - sp->me.y )*stem->unit.y;
3087 	/* Don't check StillStem for non-curved segments, as they are subject */
3088 	/* to further projection-related tests anyway */
3089 	if ( off > min && off < max && ( !curved ||
3090 	    ( fabs( dist ) < ( width + width * ratio ) &&
3091 	    StillStem( gd,err,&pos,stem )))) {
3092 
3093 	    good = pos;
3094 	    t += incr;
3095 	} else
3096 	    t -= incr;
3097 	incr/=2;
3098     }
3099     *res = good;
3100 return( curved );
3101 }
3102 
AdjustForImperfectSlopeMatch(SplinePoint * sp,BasePoint * pos,BasePoint * newpos,struct stemdata * stem,int is_l)3103 static int AdjustForImperfectSlopeMatch( SplinePoint *sp,BasePoint *pos,
3104     BasePoint *newpos,struct stemdata *stem,int is_l ) {
3105 
3106     double poff, err, min, max;
3107     BasePoint *base;
3108 
3109     base = ( is_l ) ? &stem->left : &stem->right;
3110     err = ( IsUnitHV( &stem->unit,true )) ? dist_error_hv : dist_error_diag;
3111     min = ( is_l ) ? stem->lmax - 2*err : stem->rmax - 2*err;
3112     max = ( is_l ) ? stem->lmin + 2*err : stem->rmin + 2*err;
3113 
3114     /* Possible if the stem unit has been attached to a line. It is */
3115     /* hard to prevent this */
3116     if ( min > max ) {
3117 	min = stem->lmin; max = stem->lmax;
3118     }
3119 
3120     poff =  ( pos->x - base->x )*stem->l_to_r.x +
3121 	    ( pos->y - base->y )*stem->l_to_r.y;
3122     if ( poff > min && poff < max ) {
3123 	*newpos = *pos;
3124 return( false );
3125     } else if ( poff <= min )
3126 	err = fabs( min );
3127     else if ( poff >= max )
3128 	err = fabs( max );
3129 
3130     newpos->x = sp->me.x + err*( pos->x - sp->me.x )/fabs( poff );
3131     newpos->y = sp->me.y + err*( pos->y - sp->me.y )/fabs( poff );
3132 return( true );
3133 }
3134 
AddLineSegment(struct stemdata * stem,struct segment * space,int cnt,int is_l,struct pointdata * pd,int base_next,struct glyphdata * gd)3135 static int AddLineSegment( struct stemdata *stem,struct segment *space,int cnt,
3136     int is_l,struct pointdata *pd,int base_next,struct glyphdata *gd ) {
3137 
3138     double s, e, t, dot;
3139     BasePoint stemp, etemp;
3140     BasePoint *start, *end, *par_unit;
3141     int same_dir, corner = 0, par;
3142     int scurved = false, ecurved = false, c, hv;
3143     SplinePoint *sp, *psp, *nsp;
3144     double b;
3145     uint8 extr;
3146 
3147     if ( pd==NULL || (sp = pd->sp)==NULL || sp->ticked ||
3148 	    sp->next==NULL || sp->prev==NULL )
3149 return( cnt );
3150     end = &sp->me;
3151     start = &sp->me;
3152     par_unit = ( base_next ) ? &pd->nextunit : &pd->prevunit;
3153     /* Do the spline and the stem unit point in the same direction ? */
3154     dot =   ( stem->unit.x * par_unit->x ) +
3155 	    ( stem->unit.y * par_unit->y );
3156     same_dir = (( dot > 0 && base_next ) || ( dot < 0 && !base_next ));
3157     if ( stem->unit.x == 1 ) corner = pd->y_corner;
3158     else if ( stem->unit.y == 1 ) corner = pd->x_corner;
3159 
3160     dot =   ( stem->unit.x * pd->nextunit.x ) +
3161 	    ( stem->unit.y * pd->nextunit.y );
3162     /* We used to apply normal checks only if the point's unit vector pointing */
3163     /* in the direction we are going to check is nearly parallel to the stem unit. */
3164     /* But this is not the best method, because a spline, "parallel" to our */
3165     /* stem, may actually have filled space at a wrong side. On the other hand, */
3166     /* sometimes it makes sense to calculate active space even for splines */
3167     /* connected to our base point under an angle which is too large to consider */
3168     /* the direction "parallel". So now we check the units' direction first */
3169     /* and then (just for straight splines) also their parallelity. */
3170     if (( dot > 0 && same_dir ) || ( dot < 0 && !same_dir )) {
3171 	/* If the segment sp-start doesn't have exactly the right slope, then */
3172 	/*  we can only use that bit of it which is less than a standard error */
3173 	par = UnitsParallel( &stem->unit,&pd->nextunit,0 );
3174 	if ( !sp->next->knownlinear ) {
3175 	    ecurved = WalkSpline( gd,pd,true,stem,is_l,par,&etemp );
3176 	    /* Can merge, but treat as curved relatively to projections */
3177 	    if ( !ecurved ) ecurved = 2;
3178 	    end = &etemp;
3179 	} else if ( par || corner )  {
3180 	    nsp = sp->next->to;
3181 	    ecurved = AdjustForImperfectSlopeMatch( sp,&nsp->me,&etemp,stem,is_l );
3182 	    end = &etemp;
3183 	}
3184     }
3185     dot =   ( stem->unit.x * pd->prevunit.x ) +
3186 	    ( stem->unit.y * pd->prevunit.y );
3187     if (( dot < 0 && same_dir ) || ( dot > 0 && !same_dir )) {
3188 	par = UnitsParallel( &stem->unit,&pd->prevunit,0 );
3189 	if ( !sp->prev->knownlinear ) {
3190 	    scurved = WalkSpline( gd,pd,false,stem,is_l,par,&stemp );
3191 	    if ( !scurved ) scurved = 2;
3192 	    start = &stemp;
3193 	} else if ( par || corner ) {
3194 	    psp = sp->prev->from;
3195 	    scurved = AdjustForImperfectSlopeMatch( sp,&psp->me,&stemp,stem,is_l );
3196 	    start = &stemp;
3197 	}
3198     }
3199     sp->ticked = true;
3200 
3201     s = (start->x-stem->left.x)*stem->unit.x +
3202 	(start->y-stem->left.y)*stem->unit.y;
3203     e = (  end->x-stem->left.x)*stem->unit.x +
3204 	(  end->y-stem->left.y)*stem->unit.y;
3205     b = (sp->me.x-stem->left.x)*stem->unit.x +
3206 	(sp->me.y-stem->left.y)*stem->unit.y;
3207 
3208     if ( s == e )
3209 return( cnt );
3210     if ( s > e ) {
3211 	t = s; c = scurved;
3212 	s = e; e = t;
3213 	scurved = ecurved; ecurved = c;
3214     }
3215     space[cnt].start = s;
3216     space[cnt].end = e;
3217     space[cnt].sbase = space[cnt].ebase = b;
3218     space[cnt].scurved = scurved;
3219     space[cnt].ecurved = ecurved;
3220 
3221     hv = IsUnitHV( &stem->unit,true );
3222     if ( hv ) {
3223 	/* For vertical/horizontal stems we assign a special meaning to  */
3224 	/* the 'curved' field. It will be non-zero if the key point of   */
3225 	/* this segment is positioned on a prominent curve:              */
3226 	/* 1 if the inner side of that curve is inside of the contour    */
3227 	/* and 2 otherwise. */
3228 	/* Later, if we get a pair of "inner" and "outer" curves, then   */
3229 	/* we are probably dealing with a feature's bend which should be */
3230 	/* necessarily marked with a hint. Checks we apply for this type */
3231 	/* of curved segments should be less strict than in other cases. */
3232 	extr = ( hv == 1 ) ? pd->y_extr : pd->x_extr;
3233 	space[cnt].curved = extr;
3234     } else {
3235 	/* For diagonal stems we consider a segment "curved" if both its */
3236 	/* start and end are curved. Curved segments usually cannot be   */
3237 	/* merged (unless scurved or ecurved is equal to 2) and are not  */
3238 	/* checked for "projections". */
3239 	space[cnt].curved = scurved && ecurved;
3240     }
3241 return( cnt+1 );
3242 }
3243 
InActive(double projection,struct segment * segments,int cnt)3244 static int InActive(double projection,struct segment *segments, int cnt) {
3245     int i;
3246 
3247     for ( i=0; i<cnt; ++i ) {
3248 	if ( projection>=segments[i].start && projection<=segments[i].end )
3249 return( true );
3250     }
3251 return( false );
3252 }
3253 
MergeSegments(struct segment * space,int cnt)3254 static int MergeSegments(struct segment *space, int cnt) {
3255     int i,j;
3256     double middle;
3257 
3258     for ( i=j=0; i<cnt; ++i, ++j ) {
3259 	if ( i!=j )
3260 	    space[j] = space[i];
3261 	while ( i+1<cnt && space[i+1].start<space[j].end ) {
3262 	    if ( space[i+1].end >= space[j].end ) {
3263 
3264 		/* If there are 2 overlapping segments and neither the  */
3265 		/* end of the first segment nor the start of the second */
3266 		/* one are curved we can merge them. Otherwise we have  */
3267 		/* to preserve them both, but modify their start/end properties */
3268 		/* so that the overlap is removed */
3269 		if ( space[j].ecurved != 1 && space[i+1].scurved != 1 ) {
3270 		    space[j].end = space[i+1].end;
3271 		    space[j].ebase = space[i+1].ebase;
3272 		    space[j].ecurved = space[i+1].ecurved;
3273 		    space[j].curved = false;
3274 		} else if ( space[j].ecurved != 1 && space[i+1].scurved == 1 ) {
3275 		    space[i+1].start = space[j].end;
3276 		    --i;
3277 		} else if ( space[j].ecurved == 1 && space[i+1].scurved != 1 ) {
3278 		    space[j].end = space[i+1].start;
3279 		    --i;
3280 		} else {
3281 		    middle = (space[j].end + space[i+1].start)/2;
3282 		    space[j].end = space[i+1].start = middle;
3283 		    --i;
3284 		}
3285 	    }
3286 	    ++i;
3287 	}
3288     }
3289 return( j );
3290 }
3291 
MergeSegmentsFinal(struct segment * space,int cnt)3292 static int MergeSegmentsFinal( struct segment *space, int cnt ) {
3293     int i,j;
3294 
3295     for ( i=j=0; i<cnt; ++i, ++j ) {
3296 	if ( i!=j )
3297 	    space[j] = space[i];
3298 	while ( i+1<cnt && space[i+1].start<=space[j].end ) {
3299 	    if ( space[i+1].end>space[j].end ) {
3300 		space[j].end = space[i+1].end;
3301 		space[j].ebase = space[i+1].ebase;
3302 		space[j].ecurved = space[i+1].ecurved;
3303 		space[j].curved = false;
3304 	    }
3305 	    ++i;
3306 	}
3307     }
3308 return( j );
3309 }
3310 
FigureStemActive(struct glyphdata * gd,struct stemdata * stem)3311 static void FigureStemActive( struct glyphdata *gd, struct stemdata *stem ) {
3312     int i, j, pcnt=0;
3313     struct pointdata *pd, **pspace = gd->pspace;
3314     struct stem_chunk *chunk;
3315     struct segment *lspace = gd->lspace, *rspace = gd->rspace;
3316     struct segment *bothspace = gd->bothspace, *activespace = gd->activespace;
3317     int lcnt, rcnt, bcnt, bpos, acnt, cove, startset, endset;
3318     double middle, width, len, clen, gap, lseg, rseg;
3319     double err, lmin, rmax, loff, roff, last, s, e, sbase, ebase;
3320     double proj, proj2, proj3, orig_proj, ptemp;
3321 
3322     width = stem->width;
3323 
3324     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL )
3325 	gd->points[i].sp->ticked = false;
3326 
3327     lcnt = rcnt = 0;
3328     for ( i=0; i<stem->chunk_cnt; ++i ) {
3329 	chunk = &stem->chunks[i];
3330 	if ( chunk->stemcheat )
3331     continue;
3332 	lcnt = AddLineSegment( stem,lspace,lcnt,true ,chunk->l,chunk->lnext,gd );
3333 	rcnt = AddLineSegment( stem,rspace,rcnt,false,chunk->r,chunk->rnext,gd );
3334     }
3335     bcnt = 0;
3336     if ( lcnt!=0 && rcnt!=0 ) {
3337 	/* For curved segments we can extend left and right active segments    */
3338 	/* a bit to ensure that they do overlap and thus can be marked with an */
3339 	/* active zone */
3340 	if ( rcnt == lcnt && stem->chunk_cnt == lcnt ) {
3341 	    for ( i=0; i<lcnt; i++ ) {
3342 		/* If it's a feature bend, then our tests should be more liberal */
3343 		cove = (( rspace[i].curved + lspace[i].curved ) == 3 );
3344 		gap = 0;
3345 		if ( lspace[i].start>rspace[i].end && lspace[i].scurved && rspace[i].ecurved )
3346 		    gap = lspace[i].start-rspace[i].end;
3347 		else if ( rspace[i].start>lspace[i].end && rspace[i].scurved && lspace[i].ecurved )
3348 		    gap = rspace[i].start-lspace[i].end;
3349 		else if ( !cove )
3350 	    continue;
3351 
3352 		lseg = lspace[i].end - lspace[i].start;
3353 		rseg = rspace[i].end - rspace[i].start;
3354 		if (( cove && gap < (lseg > rseg ? lseg : rseg )) ||
3355 		    ( gap < ( lseg + rseg )/2 && !stem->chunks[i].stub )) {
3356 		    if ( lspace[i].ebase<rspace[i].start )
3357 			rspace[i].start = lspace[i].ebase;
3358 		    else if ( lspace[i].sbase>rspace[i].end )
3359 			rspace[i].end = lspace[i].sbase;
3360 		    if ( rspace[i].ebase<lspace[i].start )
3361 			lspace[i].start = rspace[i].ebase;
3362 		    else if ( rspace[i].sbase>lspace[i].end )
3363 			lspace[i].end = rspace[i].sbase;
3364 		}
3365 	    }
3366 	}
3367 	qsort(lspace,lcnt,sizeof(struct segment),segment_cmp);
3368 	qsort(rspace,rcnt,sizeof(struct segment),segment_cmp);
3369 	lcnt = MergeSegments( lspace,lcnt );
3370 	rcnt = MergeSegments( rspace,rcnt );
3371 	for ( i=j=bcnt=0; i<lcnt && j<rcnt; ++i ) {
3372 	    while ( j<rcnt && rspace[j].end<=lspace[i].start )
3373 		++j;
3374 	    while ( j<rcnt && rspace[j].start<=lspace[i].end ) {
3375 		cove = (( rspace[j].curved + lspace[i].curved ) == 3 );
3376 
3377 		s = ( rspace[j].start > lspace[i].start ) ?
3378 		    rspace[j].start : lspace[i].start;
3379 		e = ( rspace[j].end < lspace[i].end ) ?
3380 		    rspace[j].end : lspace[i].end;
3381 		sbase = ( rspace[j].start > lspace[i].start ) ?
3382 		    lspace[i].sbase : rspace[j].sbase;
3383 		ebase = ( rspace[j].end < lspace[i].end ) ?
3384 		    lspace[i].ebase : rspace[j].ebase;
3385 
3386 		middle = ( lspace[i].start + rspace[j].start )/2;
3387 		bothspace[bcnt].start = ( cove && middle < s ) ? middle : s;
3388 		if ( rspace[j].start > lspace[i].start )
3389 		    bothspace[bcnt].scurved = ( rspace[j].scurved || sbase < s ) ?
3390 			rspace[j].scurved : lspace[i].scurved;
3391 		else
3392 		    bothspace[bcnt].scurved = ( lspace[i].scurved || sbase < s ) ?
3393 			lspace[i].scurved : rspace[j].scurved;
3394 
3395 		middle = ( lspace[i].end + rspace[j].end )/2;
3396 		bothspace[bcnt].end = ( cove && middle > e ) ? middle : e;
3397 		if ( rspace[j].end < lspace[i].end )
3398 		    bothspace[bcnt].ecurved = ( rspace[j].ecurved || ebase > e ) ?
3399 			rspace[j].ecurved : lspace[i].ecurved;
3400 		else
3401 		    bothspace[bcnt].ecurved = ( lspace[i].ecurved || ebase > e ) ?
3402 			lspace[i].ecurved : rspace[j].ecurved;
3403 
3404 		sbase = ( rspace[j].sbase > lspace[i].sbase ) ?
3405 		    rspace[j].sbase : lspace[i].sbase;
3406 		ebase = ( rspace[j].ebase < lspace[i].ebase ) ?
3407 		    rspace[j].ebase : lspace[i].ebase;
3408 		if ( sbase > bothspace[bcnt].end )
3409 		    sbase = ebase = bothspace[bcnt].end;
3410 		else if ( ebase < bothspace[bcnt].start )
3411 		    sbase = ebase = bothspace[bcnt].start;
3412 		else if ( ebase < sbase )
3413 		    ebase = sbase = ( ebase + sbase )/2;
3414 		bothspace[bcnt].sbase = sbase;
3415 		bothspace[bcnt].ebase = ebase;
3416 
3417 		bothspace[bcnt++].curved = rspace[j].curved || lspace[i].curved;
3418 
3419 		if ( rspace[j].end>lspace[i].end )
3420 	    break;
3421 		++j;
3422 	    }
3423 	}
3424     }
3425 #if GLYPH_DATA_DEBUG
3426     fprintf( stderr, "Active zones for stem l=%.2f,%.2f r=%.2f,%.2f dir=%.2f,%.2f:\n",
3427 	stem->left.x,stem->left.y,stem->right.x,stem->right.y,stem->unit.x,stem->unit.y );
3428     for ( i=0; i<lcnt; i++ ) {
3429 	fprintf( stderr, "\tleft space curved=%d\n",lspace[i].curved );
3430 	fprintf( stderr, "\t\tstart=%.2f,base=%.2f,curved=%d\n",
3431 	    lspace[i].start,lspace[i].sbase,lspace[i].scurved );
3432 	fprintf( stderr, "\t\tend=%.2f,base=%.2f,curved=%d\n",
3433 	    lspace[i].end,lspace[i].ebase,lspace[i].ecurved );
3434     }
3435     for ( i=0; i<rcnt; i++ ) {
3436 	fprintf( stderr, "\tright space curved=%d\n",rspace[i].curved );
3437 	fprintf( stderr, "\t\tstart=%.2f,base=%.2f,curved=%d\n",
3438 	    rspace[i].start,rspace[i].sbase,rspace[i].scurved );
3439 	fprintf( stderr, "\t\tend=%.2f,base=%.2f,curved=%d\n",
3440 	    rspace[i].end,rspace[i].ebase,rspace[i].ecurved );
3441     }
3442     for ( i=0; i<bcnt; i++ ) {
3443 	fprintf( stderr, "\tboth space\n" );
3444 	fprintf( stderr, "\t\tstart=%.2f,base=%.2f,curved=%d\n",
3445 	    bothspace[i].start,bothspace[i].sbase,bothspace[i].scurved );
3446 	fprintf( stderr, "\t\tend=%.2f,base=%.2f,curved=%d\n",
3447 	    bothspace[i].end,bothspace[i].ebase,bothspace[i].ecurved );
3448     }
3449     fprintf( stderr,"\n" );
3450 #endif
3451 
3452     err = ( stem->unit.x == 0 || stem->unit.y == 0 ) ?
3453 	dist_error_hv : dist_error_diag;
3454     lmin = ( stem->lmin < -err ) ? stem->lmin : -err;
3455     rmax = ( stem->rmax > err ) ? stem->rmax : err;
3456     acnt = 0;
3457     if ( bcnt!=0 ) {
3458 	for ( i=0; i<gd->pcnt; ++i ) if ( (pd = &gd->points[i])->sp!=NULL ) {
3459 	    /* Let's say we have a stem. And then inside that stem we have */
3460 	    /*  another rectangle. So our first stem isn't really a stem any */
3461 	    /*  more (because we hit another edge first), yet it's still reasonable*/
3462 	    /*  to align the original stem */
3463 	    /* Now suppose the rectangle is rotated a bit so we can't make */
3464 	    /*  a stem from it. What do we do here? */
3465 	    loff =  ( pd->sp->me.x - stem->left.x ) * stem->unit.y -
3466 		    ( pd->sp->me.y - stem->left.y ) * stem->unit.x;
3467 	    roff =  ( pd->sp->me.x - stem->right.x ) * stem->unit.y -
3468 		    ( pd->sp->me.y - stem->right.y ) * stem->unit.x;
3469 
3470 	    if ( loff >= lmin && roff <= rmax ) {
3471 		pd->projection = (pd->sp->me.x - stem->left.x)*stem->unit.x +
3472 				 (pd->sp->me.y - stem->left.y)*stem->unit.y;
3473 		if ( InActive(pd->projection,bothspace,bcnt) )
3474 		    pspace[pcnt++] = pd;
3475 	    }
3476 	}
3477 	qsort(pspace,pcnt,sizeof(struct pointdata *),proj_cmp);
3478 
3479 	bpos = i = 0;
3480 	while ( bpos<bcnt ) {
3481 	    if ( bothspace[bpos].curved || pcnt==0 ) {
3482 		activespace[acnt++] = bothspace[bpos++];
3483 	    } else {
3484 		last = bothspace[bpos].start;
3485 		startset = false; endset = false;
3486 
3487  		if ( bothspace[bpos].scurved ||
3488 		    StemIsActiveAt( gd,stem,bothspace[bpos].start+0.0015 )) {
3489 
3490 		    activespace[acnt].scurved = bothspace[bpos].scurved;
3491 		    activespace[acnt].start  = bothspace[bpos].start;
3492 		    startset = true;
3493 		}
3494 
3495 		/* If the stem is preceded by a curved segment, then skip     */
3496 		/* the first point position and start from the next one.      */
3497 		/* (Otherwise StemIsActiveAt() may consider the stem is       */
3498 		/* "inactive" at the fragment between the start of the active */
3499 		/* space and the first point actually belonging to this stem) */
3500 		if ( bothspace[bpos].scurved ) {
3501 		    while ( pcnt>i && pspace[i]->projection < bothspace[bpos].sbase ) i++;
3502 
3503 		    if ( pcnt > i && pspace[i]->projection >= bothspace[bpos].sbase ) {
3504 			last = activespace[acnt].end = pspace[i]->projection;
3505 			activespace[acnt].ecurved = false;
3506 			activespace[acnt].curved = false;
3507 			endset=true;
3508 		    }
3509 		}
3510 
3511 		while ( i<pcnt && (
3512 		    ( !bothspace[bpos].ecurved && pspace[i]->projection<bothspace[bpos].end ) ||
3513 		    ( bothspace[bpos].ecurved && pspace[i]->projection<=bothspace[bpos].ebase ))) {
3514 		    if ( last==activespace[acnt].start && pspace[i]->projection >= last ) {
3515 
3516 			if ( !StemIsActiveAt( gd,stem,last+(( 1.001*pspace[i]->projection-last )/2.001 ))) {
3517 			    last = activespace[acnt].start = pspace[i]->projection;
3518 			    activespace[acnt].scurved = false;
3519 			    startset = true; endset = false;
3520 			} else {
3521 			    last = activespace[acnt].end = pspace[i]->projection;
3522 			    activespace[acnt].ecurved = false;
3523 			    activespace[acnt].curved = false;
3524 			    endset = true;
3525 			}
3526 		    } else if (( last==activespace[acnt].end || !startset )
3527 			&& pspace[i]->projection >= last) {
3528 
3529 			if ( !StemIsActiveAt( gd,stem,last+(( 1.001*pspace[i]->projection-last )/2.001 )) ||
3530 			    !startset ) {
3531 
3532 			    if ( startset ) acnt++;
3533 			    last = activespace[acnt].start = pspace[i]->projection;
3534 			    activespace[acnt].scurved = false;
3535 			    startset = true; endset = false;
3536 			} else {
3537 			    last = activespace[acnt].end = pspace[i]->projection;
3538 			    activespace[acnt].ecurved = false;
3539 			    activespace[acnt].curved = false;
3540 			    endset = true;
3541 			}
3542 		    }
3543 		    ++i;
3544 		}
3545 
3546 		if (( bothspace[bpos].ecurved ||
3547 		    StemIsActiveAt( gd,stem,bothspace[bpos].end-0.0015 )) &&
3548 		    startset ) {
3549 
3550 		    activespace[acnt].end = bothspace[bpos].end;
3551 		    activespace[acnt].ecurved = bothspace[bpos].ecurved;
3552 		    activespace[acnt].curved = bothspace[bpos].curved;
3553 		    endset = true;
3554 		}
3555 		++bpos;
3556 		if ( endset ) ++acnt;
3557 	    }
3558 	}
3559     }
3560 
3561     for ( i=0; i<stem->chunk_cnt; ++i ) {
3562 	chunk = &stem->chunks[i];
3563 	/* stemcheat 1 -- diagonal edge stem;
3564 	 *           2 -- diagonal corner stem with a sharp top;
3565 	 *           3 -- diagonal corner stem with a flat top;
3566 	 *           4 -- bounding box hint */
3567 	if ( chunk->stemcheat==3 && chunk->l!=NULL && chunk->r!=NULL &&
3568 		i+1<stem->chunk_cnt &&
3569 		stem->chunks[i+1].stemcheat==3 &&
3570 		( chunk->l==stem->chunks[i+1].l ||
3571 		chunk->r==stem->chunks[i+1].r )) {
3572 
3573 	    SplinePoint *sp = chunk->l==stem->chunks[i+1].l ?
3574 		chunk->l->sp : chunk->r->sp;
3575 	    proj =  (sp->me.x - stem->left.x) *stem->unit.x +
3576 		    (sp->me.y - stem->left.y) *stem->unit.y;
3577 
3578 	    SplinePoint *sp2 = chunk->l==stem->chunks[i+1].l ?
3579 		chunk->r->sp : chunk->l->sp;
3580 	    SplinePoint *sp3 = chunk->l==stem->chunks[i+1].l ?
3581 		stem->chunks[i+1].r->sp : stem->chunks[i+1].l->sp;
3582 	    proj2 = (sp2->me.x - stem->left.x) *stem->unit.x +
3583 		    (sp2->me.y - stem->left.y) *stem->unit.y;
3584 	    proj3 = (sp3->me.x - stem->left.x) *stem->unit.x +
3585 		    (sp3->me.y - stem->left.y) *stem->unit.y;
3586 
3587 	    if ( proj2>proj3 ) {
3588 		ptemp = proj2; proj2 = proj3; proj3 = ptemp;
3589 	    }
3590 
3591 	    if ( (proj3-proj2) < width ) {
3592 		activespace[acnt  ].curved = true;
3593 		proj2 -= width/2;
3594 		proj3 += width/2;
3595 	    } else {
3596 		activespace[acnt  ].curved = false;
3597 	    }
3598 
3599 	    activespace[acnt].start = proj2;
3600 	    activespace[acnt].end = proj3;
3601 	    activespace[acnt].sbase = activespace[acnt].ebase = proj;
3602 	    acnt++;
3603 	    ++i;
3604 	} else if ( chunk->stemcheat && chunk->l!=NULL && chunk->r!=NULL ) {
3605 	    SplinePoint *sp = chunk->l->sp;
3606 	    proj =  ( sp->me.x - stem->left.x ) * stem->unit.x +
3607 		    ( sp->me.y - stem->left.y ) * stem->unit.y;
3608 	    orig_proj = proj;
3609 	    SplinePoint *other = chunk->lnext ? sp->next->to : sp->prev->from;
3610 	    len  =  (other->me.x - sp->me.x) * stem->unit.x +
3611 		    (other->me.y - sp->me.y) * stem->unit.y;
3612 	    if ( chunk->stemcheat == 2 )
3613 		proj -= width/2;
3614 	    else if ( len<0 )
3615 		proj -= width;
3616 	    activespace[acnt].curved = true;
3617 	    activespace[acnt].start = proj;
3618 	    activespace[acnt].end = proj+width;
3619 	    activespace[acnt].sbase = activespace[acnt].ebase = orig_proj;
3620 	    acnt++;
3621 	}
3622     }
3623 
3624     if ( acnt!=0 ) {
3625 	stem->activecnt = MergeSegmentsFinal( activespace,acnt );
3626 	stem->active = malloc(acnt*sizeof(struct segment));
3627 	memcpy(stem->active,activespace,acnt*sizeof(struct segment));
3628     }
3629 
3630     len = clen = 0;
3631     for ( i=0; i<acnt; ++i ) {
3632 	if ( stem->active[i].curved )
3633 	    clen += stem->active[i].end-stem->active[i].start;
3634 	else
3635 	    len += stem->active[i].end-stem->active[i].start;
3636     }
3637     stem->len = len; stem->clen = len+clen;
3638 }
3639 
GDStemsFixupIntersects(struct glyphdata * gd)3640 static void GDStemsFixupIntersects(struct glyphdata *gd) {
3641     int i, j, stemidx;
3642     struct stemdata *stem;
3643     struct stem_chunk *chunk;
3644 
3645     for ( i=0; i<gd->stemcnt; ++i ) {
3646 	stem = &gd->stems[i];
3647 	for ( j=0; j<stem->chunk_cnt; ++j ) {
3648 	    chunk = &stem->chunks[j];
3649 	    if ( chunk->l!=NULL ) {
3650 		stemidx = IsStemAssignedToPoint( chunk->l,stem,true );
3651 		FixupT( chunk->l,stemidx,true,chunk->l_e_idx );
3652 		stemidx = IsStemAssignedToPoint( chunk->l,stem,false );
3653 		FixupT( chunk->l,stemidx,false,chunk->l_e_idx );
3654 	    }
3655 	    if ( chunk->r!=NULL ) {
3656 		stemidx = IsStemAssignedToPoint( chunk->r,stem,true );
3657 		FixupT( chunk->r,stemidx,true,chunk->r_e_idx );
3658 		stemidx = IsStemAssignedToPoint( chunk->r,stem,false );
3659 		FixupT( chunk->r,stemidx,false,chunk->r_e_idx );
3660 	    }
3661 	}
3662     }
3663 }
3664 
StemsWouldConflict(struct stemdata * stem1,struct stemdata * stem2)3665 static int StemsWouldConflict( struct stemdata *stem1,struct stemdata *stem2 ) {
3666     double loff, roff, s1, s2, e1, e2;
3667     int acnt1, acnt2;
3668 
3669     if ( stem1 == stem2 || !UnitsParallel( &stem1->unit,&stem2->unit,true ))
3670 return( false );
3671 
3672     loff = ( stem2->left.x - stem1->left.x ) * stem1->unit.y -
3673 	   ( stem2->left.y - stem1->left.y ) * stem1->unit.x;
3674     roff = ( stem2->right.x - stem1->right.x ) * stem1->unit.y -
3675 	   ( stem2->right.y - stem1->right.y ) * stem1->unit.x;
3676     loff = fabs( loff ); roff = fabs( roff );
3677     if ( loff > stem1->width || roff > stem1->width )
3678 return( false );
3679 
3680     acnt1 = stem1->activecnt;
3681     acnt2 = stem2->activecnt;
3682     if ( acnt1 == 0 || acnt2 == 0 )
3683 return( false );
3684     s1 = stem1->active[0].start; e1 = stem1->active[acnt1-1].end;
3685     s2 = stem2->active[0].start; e2 = stem2->active[acnt2-1].end;
3686 
3687     loff = ( stem2->left.x - stem1->left.x ) * stem1->unit.x +
3688 	   ( stem2->left.y - stem1->left.y ) * stem1->unit.y;
3689     if (( s2+loff >= s1 && s2+loff <= e1 ) || ( e2+loff >= s1 && e2+loff <= e1 ) ||
3690 	( s2+loff <= s1 && e2+loff >= e1 ) || ( e2+loff <= s1 && s2+loff >= e1 ))
3691 return( true );
3692 
3693 return( false );
3694 }
3695 
3696 /* Convert diagonal stems generated for stubs and intersections to horizontal */
3697 /* or vertical, if they have just one chunk. This should be done before calculating */
3698 /* active zones, as they are calculated against each stem's unit vector */
GDNormalizeStubs(struct glyphdata * gd)3699 static void GDNormalizeStubs( struct glyphdata *gd ) {
3700     int i, j, hv;
3701     struct stemdata *stem;
3702     struct stem_chunk *chunk;
3703     BasePoint newdir;
3704 
3705     for ( i=0; i<gd->stemcnt; ++i ) {
3706 	stem = &gd->stems[i];
3707 	if ( stem->positioned )
3708     continue;
3709 
3710 	if ( !IsUnitHV( &stem->unit,true )) {
3711 	    hv = IsUnitHV( &stem->unit,false );
3712 	    if ( hv && StemFitsHV( stem,( hv == 1 ),3 )) {
3713 		if ( hv == 2 && stem->unit.y < 0 )
3714 		    SwapEdges( gd,stem );
3715 
3716 		newdir.x = fabs( rint( stem->unit.x ));
3717 		newdir.y = fabs( rint( stem->unit.y ));
3718 		SetStemUnit( stem,newdir );
3719 
3720 		for ( j=0; j<stem->chunk_cnt && stem->leftidx == -1 && stem->rightidx == -1; j++ ) {
3721 		    chunk = &stem->chunks[j];
3722 
3723 		    if ( stem->leftidx == -1 && chunk->l != NULL )
3724 			stem->leftidx = GetValidPointDataIndex( gd,chunk->l->sp,stem );
3725 		    if ( stem->rightidx == -1 && chunk->r != NULL )
3726 			stem->rightidx = GetValidPointDataIndex( gd,chunk->r->sp,stem );
3727 		}
3728 	    }
3729 	}
3730     }
3731 }
3732 
GDFindUnlikelyStems(struct glyphdata * gd)3733 static void GDFindUnlikelyStems( struct glyphdata *gd ) {
3734     double width, minl, ratio;
3735     int i, j, k, stem_cnt, ls_cnt, rs_cnt, ltick, rtick;
3736     struct pointdata *lpd, *rpd;
3737     Spline *ls, *rs;
3738     SplinePoint *lsp, *rsp;
3739     BasePoint *lunit, *runit, *slunit, *srunit, *sunit;
3740     struct stemdata *stem, *stem1, *tstem;
3741     struct stemdata **tstems, **lstems, **rstems;
3742     struct stem_chunk *chunk;
3743 
3744     GDStemsFixupIntersects( gd );
3745 
3746     for ( i=0; i<gd->stemcnt; ++i ) {
3747 	stem = &gd->stems[i];
3748 
3749 	/* If stem had been already present in the spline char before we */
3750 	/* started generating glyph data, then it should never be */
3751 	/* considered "too big" */
3752 	if ( stem->positioned )
3753     continue;
3754 
3755 	/* If a stem has straight edges, and it is wider than tall */
3756 	/*  then it is unlikely to be a real stem */
3757 	width = stem->width;
3758 	ratio = IsUnitHV( &stem->unit,true ) ? gd->emsize/( 6 * width ) : -0.25;
3759 	stem->toobig =  ( stem->clen + stem->clen * ratio < width );
3760     }
3761 
3762     /* One more check for curved stems. If a stem has just one active */
3763     /* segment, this segment is curved and the stem has no conflicts, */
3764     /* then select the active segment length which allows us to consider */
3765     /* this stem suitable for PS output by such a way, that stems connecting */
3766     /* the opposite sides of a circle are always accepted */
3767     for ( i=0; i<gd->stemcnt; ++i ) if ( gd->stems[i].toobig ) {
3768 	stem = &gd->stems[i];
3769 	width = stem->width;
3770 
3771 	if ( IsUnitHV( &stem->unit,true ) && stem->activecnt == 1 &&
3772 	    stem->active[0].curved && width/2 > dist_error_curve ) {
3773 
3774 	    for ( j=0; j<gd->stemcnt; ++j) {
3775 		stem1 = &gd->stems[j];
3776 
3777 		if ( !stem1->toobig && StemsWouldConflict( stem,stem1 ))
3778 	    break;
3779 	    }
3780 
3781 	    if ( j == gd->stemcnt ) {
3782 		minl = sqrt( pow( width/2,2 ) - pow( width/2 - dist_error_curve,2 ));
3783 		if ( stem->clen >= minl ) stem->toobig = false;
3784 	    }
3785 	}
3786     }
3787 
3788     /* And finally a check for stubs and feature terminations. We don't */
3789     /* want such things to be controlled by any special hints, if there */
3790     /* is already a hint controlling the middle of the same feature */
3791     for ( i=0; i<gd->stemcnt; ++i ) {
3792 	stem = &gd->stems[i];
3793 	if ( stem->positioned )
3794     continue;
3795 
3796 	if ( stem->chunk_cnt == 1 && stem->chunks[0].stub & 3 ) {
3797 	    chunk = &stem->chunks[0];
3798 	    slunit = chunk->lnext ? &chunk->l->nextunit : &chunk->l->prevunit;
3799 	    srunit = chunk->rnext ? &chunk->r->nextunit : &chunk->r->prevunit;
3800 
3801 	    /* This test is valid only for features which are not exactly horizontal/    */
3802 	    /* vertical. But we can't check this using the stem unit, as it may have     */
3803 	    /* already beeen reset to HV. So we use the units of this stem's base points */
3804 	    /* instead. */
3805 	    if ( IsUnitHV( slunit,true ) && IsUnitHV( srunit,true ))
3806     continue;
3807 	    if ( UnitCloserToHV( srunit,slunit ) > 0 ) sunit = srunit;
3808 	    else sunit = slunit;
3809 
3810 	    lpd = chunk->l; lsp = lpd->sp; lstems = tstems = NULL;
3811 	    ls_cnt = 0;
3812 	    do {
3813 		stem_cnt = (( chunk->lnext && lpd == chunk->l ) ||
3814 			    ( !chunk->lnext && lpd != chunk->l )) ? lpd->nextcnt : lpd->prevcnt;
3815 		for ( j=0; j<stem_cnt; j++ ) {
3816 		    tstems= (( chunk->lnext && lpd == chunk->l ) ||
3817 			    ( !chunk->lnext && lpd != chunk->l )) ? lpd->nextstems : lpd->prevstems;
3818 		    tstem = tstems[j];
3819 		    if ( tstem != stem ) {
3820 			lstems = tstems;
3821 			ls_cnt = stem_cnt;
3822 		break;
3823 		    }
3824 		}
3825 		if( lstems != NULL )
3826 	    break;
3827 		ls = ( chunk->lnext ) ? lsp->next : lsp->prev;
3828 		if ( ls == NULL )
3829 	    break;
3830 		lsp = ( chunk->lnext ) ? ls->to : ls->from;
3831 		lpd = &gd->points[lsp->ptindex];
3832 		lunit = ( chunk->lnext ) ? &lpd->prevunit : &lpd->nextunit;
3833 	    } while ( lpd != chunk->l && lpd != chunk->r &&
3834 		UnitsParallel( lunit,sunit,false ));
3835 
3836 	    rpd = chunk->r; rsp = rpd->sp; rstems = tstems = NULL;
3837 	    rs_cnt = 0;
3838 	    do {
3839 		stem_cnt = (( chunk->rnext && rpd == chunk->r ) ||
3840 			    ( !chunk->rnext && rpd != chunk->r )) ? rpd->nextcnt : rpd->prevcnt;
3841 		for ( j=0; j<stem_cnt; j++ ) {
3842 		    tstems= (( chunk->rnext && rpd == chunk->r ) ||
3843 			    ( !chunk->rnext && rpd != chunk->r )) ? rpd->nextstems : rpd->prevstems;
3844 		    tstem = tstems[j];
3845 		    if ( tstem != stem ) {
3846 			rstems = tstems;
3847 			rs_cnt = stem_cnt;
3848 		break;
3849 		    }
3850 		}
3851 		if( rstems != NULL )
3852 	    break;
3853 		rs = ( chunk->rnext ) ? rsp->next : rsp->prev;
3854 		if ( rs == NULL )
3855 	    break;
3856 		rsp = ( chunk->rnext ) ? rs->to : rs->from;
3857 		rpd = &gd->points[rsp->ptindex];
3858 		runit = ( chunk->rnext ) ? &rpd->prevunit : &rpd->nextunit;
3859 	    } while ( rpd != chunk->r && rpd != chunk->l &&
3860 		UnitsParallel( runit,sunit,false ));
3861 
3862 	    if ( lstems != NULL && rstems !=NULL ) {
3863 		for ( j=0; j<ls_cnt && !stem->toobig; j++ ) {
3864 		    for ( k=0; k<rs_cnt && !stem->toobig; k++ ) {
3865 			if ( lstems[j] == rstems[k] && IsUnitHV( &lstems[j]->unit,true )) {
3866 			    stem->toobig = true;
3867 			}
3868 		    }
3869 		}
3870 	    }
3871 	}
3872 
3873 	/* One more check for intersections between a curved segment and a */
3874 	/* straight feature. Imagine a curve intersected by two bars, like in a Euro */
3875 	/* glyph. Very probably we will get two chunks, one controlling the uppest   */
3876 	/* two points of intersection, and another the lowest two, and most probably */
3877 	/* these two chunks will get merged into a single stem (so this stem will    */
3878 	/* even get an exactly vertical vector). Yet we don't need this stem because */
3879 	/* there is already a stem controlling the middle of the curve (between two  */
3880 	/* bars).*/
3881 	else if ( stem->chunk_cnt == 2 &&
3882 	    (( stem->chunks[0].stub & 7 && stem->chunks[1].stub & 6 ) ||
3883 	     ( stem->chunks[0].stub & 6 && stem->chunks[1].stub & 7 ))) {
3884 	    for ( j=0; j<gd->stemcnt; ++j) {
3885 		stem1 = &gd->stems[j];
3886 		if ( !stem1->toobig && StemsWouldConflict( stem,stem1 ))
3887 	    break;
3888 	    }
3889 
3890 	    if ( j < gd->stemcnt )
3891 		stem->toobig = true;
3892 	}
3893     }
3894 
3895     for ( i=0; i<gd->stemcnt; ++i ) {
3896 	stem = &gd->stems[i];
3897 	if ( IsUnitHV( &stem->unit,true ))
3898     continue;
3899 
3900 	/* If a diagonal stem doesn't have at least 2 points assigned to   */
3901 	/* each edge, then we probably can't instruct it. However we don't */
3902 	/* disable stems which have just one point on each side, if those  */
3903 	/* points are inflection points, as such stems may be useful for   */
3904 	/* metafont routines */
3905 	if ( stem->lpcnt < 2 || stem->rpcnt < 2 ) {
3906 	    lpd = rpd = NULL;
3907 	    for ( j=0; j<stem->chunk_cnt && lpd == NULL && rpd == NULL; j++ ) {
3908 		chunk = &stem->chunks[j];
3909 		if ( chunk->l != NULL ) lpd = chunk->l;
3910 		if ( chunk->r != NULL ) rpd = chunk->r;
3911 	    }
3912 	    if (lpd == NULL || rpd == NULL ||
3913 		!IsInflectionPoint( gd,lpd ) || !IsInflectionPoint( gd,rpd ) || stem->clen < stem->width )
3914 		stem->toobig = 2;
3915 	} else if ( stem->activecnt >= stem->chunk_cnt )
3916 	    stem->toobig = 2;
3917     }
3918 
3919     /* When using preexisting stem data, occasionally we can get two slightly      */
3920     /* different stems (one predefined, another recently detected) with nearly     */
3921     /* parallel vectors, sharing some points at both sides. Attempting to instruct */
3922     /* them both would lead to very odd effects. So we must disable one */
3923     for ( i=0; i<gd->stemcnt; ++i ) {
3924 	stem = &gd->stems[i];
3925 	if ( !stem->positioned || IsUnitHV( &stem->unit,true ))
3926     continue;
3927 
3928 	for ( j=0; j<gd->stemcnt; ++j ) {
3929 	    tstem = &gd->stems[j];
3930 	    if ( tstem == stem || tstem->toobig || !UnitsParallel( &stem->unit,&tstem->unit,false ))
3931 	continue;
3932 
3933 	    ltick = false; rtick = false;
3934 	    for ( k=0; k<stem->chunk_cnt && ( !ltick || !rtick ); k++ ) {
3935 		chunk =  &stem->chunks[k];
3936 
3937 		if ( chunk->l != NULL &&
3938 		    IsStemAssignedToPoint( chunk->l,stem ,chunk->lnext ) != -1 &&
3939 		    IsStemAssignedToPoint( chunk->l,tstem,chunk->lnext ) != -1 )
3940 		    ltick = true;
3941 		if ( chunk->r != NULL &&
3942 		    IsStemAssignedToPoint( chunk->r,stem ,chunk->rnext ) != -1 &&
3943 		    IsStemAssignedToPoint( chunk->r,tstem,chunk->rnext ) != -1 )
3944 		    rtick = true;
3945 	    }
3946 	    if ( ltick && rtick ) tstem->toobig = 2;
3947 	}
3948     }
3949 }
3950 
StemPointOnDiag(struct glyphdata * gd,struct stemdata * stem,struct pointdata * pd)3951 static int StemPointOnDiag( struct glyphdata *gd,struct stemdata *stem,
3952     struct pointdata *pd ) {
3953 
3954     struct stemdata *tstem;
3955     int i, is_next, stemcnt;
3956 
3957     if ( gd->only_hv || pd->colinear )
3958 return( false );
3959 
3960     is_next = IsStemAssignedToPoint( pd,stem,false ) != -1;
3961     stemcnt = ( is_next ) ? pd->nextcnt : pd->prevcnt;
3962 
3963     for ( i=0; i<stemcnt; i++ ) {
3964 	tstem = ( is_next ) ? pd->nextstems[i] : pd->prevstems[i];
3965 	if ( !IsUnitHV( &tstem->unit,true ) &&
3966 	    tstem->lpcnt >= 2 && tstem->rpcnt >=2 )
3967 return( true );
3968     }
3969 return( false );
3970 }
3971 
FindRefPointsExisting(struct glyphdata * gd,struct stemdata * stem)3972 static void FindRefPointsExisting( struct glyphdata *gd,struct stemdata *stem ) {
3973     int i;
3974     int pos, lbase, rbase, is_x;
3975     struct stem_chunk *chunk;
3976     struct pointdata *pd;
3977 
3978     is_x = (int) rint( stem->unit.y );
3979     lbase = ((real *) &stem->left.x)[!is_x];
3980     rbase = ((real *) &stem->right.x)[!is_x];
3981 
3982     for ( i=0; i<stem->chunk_cnt; ++i ) {
3983 	chunk = &stem->chunks[i];
3984 
3985 	if ( chunk->ltick ) {
3986 	    pd = chunk->l;
3987 	    pos = ((real *) &pd->sp->me.x)[!is_x];
3988 	    if ( pos == lbase ) {
3989 		pd->value++;
3990 		if ( pd->sp->ptindex < gd->realcnt )
3991 		    pd->value++;
3992 		if ( StemPointOnDiag( gd,stem,pd ))
3993 		    pd->value++;
3994 	    }
3995 	}
3996 
3997 	if ( chunk->rtick ) {
3998 	    pd = chunk->r;
3999 	    pos = ((real *) &pd->sp->me.x)[!is_x];
4000 	    if ( pos == rbase ) {
4001 		pd->value++;
4002 		if ( pd->sp->ptindex < gd->realcnt )
4003 		    pd->value++;
4004 		if ( StemPointOnDiag( gd,stem,pd ))
4005 		    pd->value++;
4006 	    }
4007 	}
4008     }
4009 }
4010 
FindRefPointsNew(struct glyphdata * gd,struct stemdata * stem)4011 static void FindRefPointsNew( struct glyphdata *gd,struct stemdata *stem ) {
4012     int i, j;
4013     int pos, lpos, rpos, testpos, is_x;
4014     int lval, rval;
4015     struct stem_chunk *chunk;
4016     struct pointdata *lmost1, *lmost2, *rmost1, *rmost2;
4017     double llen, prevllen, rlen, prevrlen;
4018     SplinePoint *sp, *tsp;
4019     uint8 *lextr, *rextr;
4020 
4021     is_x = (int) rint( stem->unit.y );
4022     lpos = ((real *) &stem->left.x)[!is_x];
4023     rpos = ((real *) &stem->right.x)[!is_x];
4024 
4025     lmost1 = rmost1 = lmost2 = rmost2 = NULL;
4026     llen = prevllen = rlen = prevrlen = 0;
4027     for ( i=0; i<stem->chunk_cnt; ++i ) {
4028 	chunk = &stem->chunks[i];
4029 
4030 	if ( chunk->ltick ) {
4031 	    sp = chunk->l->sp;
4032 	    pos = ((real *) &sp->me.x)[!is_x];
4033 	    lval = 0;
4034 	    for ( j=0; j<i; j++ ) if ( stem->chunks[j].ltick ) {
4035 		tsp = stem->chunks[j].l->sp;
4036 		testpos = ((real *) &tsp->me.x)[!is_x];
4037 		if ( pos == testpos ) {
4038 		    lval = stem->chunks[j].l->value;
4039 		    stem->chunks[j].l->value++;
4040 		    /* An additional bonus for points which form together */
4041 		    /* a longer stem segment */
4042 		    if ( sp->next->to == tsp || sp->prev->from == tsp ) {
4043 			llen = fabs(( sp->me.x - tsp->me.x )*stem->unit.x +
4044 				    ( sp->me.y - tsp->me.y )*stem->unit.y );
4045 			if ( llen > prevllen ) {
4046 			    lmost1 = stem->chunks[j].l;
4047 			    lmost2 = chunk->l;
4048 			    prevllen = llen;
4049 			}
4050 		    }
4051 		}
4052 	    }
4053 	    chunk->l->value = lval+1;
4054 
4055 	    if ( lval == 0 &&
4056 		( stem->lmin - ( pos - lpos ) > -dist_error_hv ) &&
4057 		( stem->lmax - ( pos - lpos ) < dist_error_hv ))
4058 		chunk->l->value++;
4059 	}
4060 
4061 	if ( chunk->rtick ) {
4062 	    sp = chunk->r->sp;
4063 	    pos = ((real *) &sp->me.x)[!is_x];
4064 	    rval = 0;
4065 	    for ( j=0; j<i; j++ ) if ( stem->chunks[j].rtick ) {
4066 		tsp = stem->chunks[j].r->sp;
4067 		testpos = ((real *) &tsp->me.x)[!is_x];
4068 		if ( pos == testpos ) {
4069 		    rval = stem->chunks[j].r->value;
4070 		    stem->chunks[j].r->value++;
4071 		    if ( sp->next->to == tsp || sp->prev->from == tsp ) {
4072 			rlen = fabs(( sp->me.x - tsp->me.x )*stem->unit.x +
4073 				    ( sp->me.y - tsp->me.y )*stem->unit.y );
4074 			if ( rlen > prevrlen ) {
4075 			    rmost1 = stem->chunks[j].r;
4076 			    rmost2 = chunk->r;
4077 			    prevrlen = rlen;
4078 			}
4079 		    }
4080 		}
4081 	    }
4082 	    chunk->r->value = rval+1;
4083 
4084 	    if ( rval == 0 &&
4085 		( stem->rmin - ( pos - rpos ) > -dist_error_hv ) &&
4086 		( stem->rmax - ( pos - rpos ) < dist_error_hv ))
4087 		chunk->r->value++;
4088 	}
4089     }
4090     if ( lmost1 != NULL && lmost2 != NULL ) {
4091 	lmost1->value++; lmost2->value++;
4092     }
4093     if ( rmost1 != NULL && rmost2 != NULL ) {
4094 	rmost1->value++; rmost2->value++;
4095     }
4096 
4097     /* Extrema points get an additional value bonus. This should     */
4098     /* prevent us from preferring wrong points for stems controlling */
4099     /* curved segments */
4100     /* Third pass to assign bonuses to extrema points (especially    */
4101     /* to those extrema which are opposed to another extremum point) */
4102     for ( i=0; i<stem->chunk_cnt; ++i ) {
4103 	chunk = &stem->chunks[i];
4104 	if ( chunk->ltick ) {
4105 	    lextr = ( is_x ) ? &chunk->l->x_extr : &chunk->l->y_extr;
4106 	    if ( *lextr ) chunk->l->value++;
4107 	}
4108 	if ( chunk->rtick ) {
4109 	    rextr = ( is_x ) ? &chunk->r->x_extr : &chunk->r->y_extr;
4110 	    if ( *rextr ) chunk->r->value++;
4111 	}
4112 
4113 	if ( chunk->ltick && chunk->rtick ) {
4114 	    lextr = ( is_x ) ? &chunk->l->x_extr : &chunk->l->y_extr;
4115 	    rextr = ( is_x ) ? &chunk->r->x_extr : &chunk->r->y_extr;
4116 	    if ( *lextr && *rextr ) {
4117 		chunk->l->value++;
4118 		chunk->r->value++;
4119 	    }
4120 	}
4121     }
4122 }
4123 
NormalizeStem(struct glyphdata * gd,struct stemdata * stem)4124 static void NormalizeStem( struct glyphdata *gd,struct stemdata *stem ) {
4125     int i;
4126     int is_x, lval, rval, val, lset, rset, best;
4127     double loff=0, roff=0;
4128     BasePoint lold, rold;
4129     SplinePoint *lbest, *rbest;
4130     struct stem_chunk *chunk;
4131 
4132     /* First sort the stem chunks by their coordinates */
4133     if ( IsUnitHV( &stem->unit,true )) {
4134 	qsort( stem->chunks,stem->chunk_cnt,sizeof( struct stem_chunk ),chunk_cmp );
4135 	is_x = (int) rint( stem->unit.y );
4136 
4137 	/* For HV stems we have to check all chunks once more in order */
4138 	/* to figure out "left" and "right" positions most typical */
4139 	/* for this stem. We perform this by assigning a value to */
4140 	/* left and right side of this chunk. */
4141 
4142 	/* First pass to determine some point properties necessary */
4143 	/* for subsequent operations */
4144 	for ( i=0; i<stem->chunk_cnt; ++i ) {
4145 	    chunk = &stem->chunks[i];
4146 	    if ( chunk->ltick )
4147 		/* reset the point's "value" to zero */
4148 		chunk->l->value = 0;
4149 	    if ( chunk->rtick )
4150 		chunk->r->value = 0;
4151 	}
4152 
4153 	/* Second pass to check which positions relative to stem edges are */
4154 	/* most common for this stem. Each position which repeats */
4155 	/* more than once gets a plus 1 value bonus */
4156 	if ( stem->positioned ) FindRefPointsExisting( gd,stem );
4157 	else FindRefPointsNew( gd,stem );
4158 
4159 	best = -1; val = 0;
4160 	for ( i=0; i<stem->chunk_cnt; ++i ) {
4161 	    chunk = &stem->chunks[i];
4162 	    lval = ( chunk->l != NULL ) ? chunk->l->value : 0;
4163 	    rval = ( chunk->r != NULL ) ? chunk->r->value : 0;
4164 	    if ((( chunk->l != NULL && chunk->l->value > 0 &&
4165 		GetValidPointDataIndex( gd,chunk->l->sp,stem ) != -1 ) ||
4166 		( stem->ghost && stem->width == 21 )) &&
4167 		(( chunk->r != NULL && chunk->r->value > 0 &&
4168 		GetValidPointDataIndex( gd,chunk->r->sp,stem ) != -1 ) ||
4169 		( stem->ghost && stem->width == 20 )) && lval + rval > val ) {
4170 
4171 		best = i;
4172 		val = lval + rval;
4173 	    }
4174 	}
4175 	if ( best > -1 ) {
4176 	    if ( !stem->ghost || stem->width == 20 ) {
4177 		lold = stem->left;
4178 		lbest = stem->chunks[best].l->sp;
4179 		stem->left = lbest->me;
4180 		stem->leftidx = GetValidPointDataIndex( gd,lbest,stem );
4181 
4182 		/* Now assign "left" and "right" properties of the stem     */
4183 		/* to point coordinates taken from the most "typical" chunk */
4184 		/* of this stem. We also have to recalculate stem width and */
4185 		/* left/right offset values */
4186 		loff = ( stem->left.x - lold.x ) * stem->unit.y -
4187 		       ( stem->left.y - lold.y ) * stem->unit.x;
4188 		stem->lmin -= loff; stem->lmax -= loff;
4189 	    }
4190 	    if ( !stem->ghost || stem->width == 21 ) {
4191 		rold = stem->right;
4192 		rbest = stem->chunks[best].r->sp;
4193 		stem->right = rbest->me;
4194 		stem->rightidx = GetValidPointDataIndex( gd,rbest,stem );
4195 		roff = ( stem->right.x - rold.x ) * stem->unit.y -
4196 		       ( stem->right.y - rold.y ) * stem->unit.x;
4197 		stem->rmin -= roff; stem->rmax -= roff;
4198 	    }
4199 	    if ( !stem->ghost )
4200 		stem->width = ( stem->right.x - stem->left.x ) * stem->unit.y -
4201 			      ( stem->right.y - stem->left.y ) * stem->unit.x;
4202 	} else {
4203 	    for ( i=0; i<stem->chunk_cnt; ++i ) {
4204 		chunk = &stem->chunks[i];
4205 		if ( chunk->l != NULL && ( !stem->ghost || stem->width == 20 )) {
4206 		    stem->leftidx = GetValidPointDataIndex( gd,chunk->l->sp,stem );
4207 		}
4208 		if ( chunk->r != NULL && ( !stem->ghost || stem->width == 21 )) {
4209 		    stem->rightidx = GetValidPointDataIndex( gd,chunk->r->sp,stem );
4210 		}
4211 	    }
4212 	}
4213     } else {
4214 	qsort( stem->chunks,stem->chunk_cnt,sizeof( struct stem_chunk ),chunk_cmp );
4215 	lset = false; rset = false;
4216 	/* Search for a pair of points whose vectors are really parallel. */
4217 	/* This check is necessary because a diagonal stem can start from */
4218 	/* a feature termination, and our checks for such terminations    */
4219 	/* are more "liberal" than in other cases. However we don't want  */
4220 	/* considering such a pair of points basic for this stem */
4221 	for ( i=0; i<stem->chunk_cnt; ++i ) {
4222 	    chunk = &stem->chunks[i];
4223 	    BasePoint *lu, *ru;
4224 	    if ( chunk->l != NULL && chunk->r != NULL ) {
4225 		lu = chunk->lnext ? &chunk->l->nextunit : &chunk->l->prevunit;
4226 		ru = chunk->rnext ? &chunk->r->nextunit : &chunk->r->prevunit;
4227 		if ( UnitsParallel( lu,ru,true )) {
4228 		    loff =  ( chunk->l->sp->me.x - stem->left.x )*stem->l_to_r.x +
4229 			    ( chunk->l->sp->me.y - stem->left.y )*stem->l_to_r.y;
4230 		    roff =  ( chunk->r->sp->me.x - stem->right.x )*stem->l_to_r.x +
4231 			    ( chunk->r->sp->me.y - stem->right.y )*stem->l_to_r.y;
4232 		    stem->left = chunk->l->sp->me;
4233 		    stem->right = chunk->r->sp->me;
4234 		    RecalcStemOffsets( stem,&stem->unit,loff != 0,roff != 0 );
4235 	break;
4236 		}
4237 	    }
4238 	}
4239 	/* If the above check fails, just select the first point (relatively) */
4240 	/* to the stem direction both at the left and the right edge */
4241 	if ( i == stem->chunk_cnt ) for ( i=0; i<stem->chunk_cnt; ++i ) {
4242 	    chunk = &stem->chunks[i];
4243 	    if ( !lset && chunk->l != NULL ) {
4244 		loff =  ( chunk->l->sp->me.x - stem->left.x )*stem->l_to_r.x +
4245 			( chunk->l->sp->me.y - stem->left.y )*stem->l_to_r.y;
4246 		stem->left = chunk->l->sp->me;
4247 		lset = true;
4248 	    }
4249 	    if ( !rset && chunk->r != NULL ) {
4250 		roff =  ( chunk->r->sp->me.x - stem->right.x )*stem->l_to_r.x +
4251 			( chunk->r->sp->me.y - stem->right.y )*stem->l_to_r.y;
4252 		stem->right = chunk->r->sp->me;
4253 		rset = true;
4254 	    }
4255 	    if ( lset && rset ) {
4256 		RecalcStemOffsets( stem,&stem->unit,loff != 0,roff != 0 );
4257 	break;
4258 	    }
4259 	}
4260     }
4261 }
4262 
AssignPointsToBBoxHint(struct glyphdata * gd,DBounds * bounds,struct stemdata * stem,int is_v)4263 static void AssignPointsToBBoxHint( struct glyphdata *gd,DBounds *bounds,
4264     struct stemdata *stem,int is_v ) {
4265 
4266     double min, max, test, left, right;
4267     double dist, prevdist;
4268     int i, j;
4269     int lcnt=0, rcnt=0, closest;
4270     BasePoint dir;
4271     SplinePoint **lpoints, **rpoints;
4272     struct pointdata *pd, *pd1, *pd2;
4273 
4274     lpoints = calloc( gd->pcnt,sizeof( SplinePoint *));
4275     rpoints = calloc( gd->pcnt,sizeof( SplinePoint *));
4276     dir.x = !is_v; dir.y = is_v;
4277     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
4278 	pd = &gd->points[i];
4279 	min = ( is_v ) ? bounds->minx : bounds->miny;
4280 	max = ( is_v ) ? bounds->maxx : bounds->maxy;
4281 	test = ( is_v ) ? pd->base.x : pd->base.y;
4282 	if ( test >= min && test < min + dist_error_hv && (
4283 	    IsCorrectSide( gd,pd,true,is_v,&dir ) || IsCorrectSide( gd,pd,false,is_v,&dir )))
4284 	    lpoints[lcnt++] = pd->sp;
4285 	else if ( test > max - dist_error_hv && test <= max && (
4286 	    IsCorrectSide( gd,pd,true,!is_v,&dir ) || IsCorrectSide( gd,pd,false,!is_v,&dir )))
4287 	    rpoints[rcnt++] = pd->sp;
4288     }
4289     if ( lcnt > 0 && rcnt > 0 ) {
4290 	if ( stem == NULL ) {
4291 	    stem = NewStem( gd,&dir,&lpoints[0]->me,&rpoints[0]->me );
4292 	    stem->bbox = true;
4293 	    stem->len = stem->width;
4294 	    stem->leftidx = GetValidPointDataIndex( gd,lpoints[0],stem );
4295 	    stem->rightidx = GetValidPointDataIndex( gd,rpoints[0],stem );
4296 	}
4297 	for ( i=0; i<lcnt; ++i ) {
4298 	    closest = -1;
4299 	    dist = 1e4; prevdist = 1e4;
4300 	    for ( j=0; j<rcnt; ++j ) {
4301 		left = ( is_v ) ? lpoints[i]->me.y : lpoints[i]->me.x;
4302 		right = ( is_v ) ? rpoints[j]->me.y : rpoints[j]->me.x;
4303 		dist = fabs( left - right );
4304 		if ( dist < prevdist ) {
4305 		    closest = j;
4306 		    prevdist = dist;
4307 		}
4308 	    }
4309 	    pd1 = &gd->points[lpoints[i]->ptindex];
4310 	    pd2 = &gd->points[rpoints[closest]->ptindex];
4311 	    AddToStem( gd,stem,pd1,pd2,false,true,4 );
4312 	}
4313 	qsort( stem->chunks,stem->chunk_cnt,sizeof( struct stem_chunk ),chunk_cmp );
4314     }
4315     free( lpoints );
4316     free( rpoints );
4317 }
4318 
CheckForBoundingBoxHints(struct glyphdata * gd)4319 static void CheckForBoundingBoxHints( struct glyphdata *gd ) {
4320     /* Adobe seems to add hints at the bounding boxes of glyphs with no hints */
4321     int i, hv;
4322     int hcnt=0, vcnt=0;
4323     double cw, ch;
4324     struct stemdata *stem, *hstem=NULL,*vstem=NULL;
4325     DBounds bounds;
4326 
4327     SplineCharFindBounds( gd->sc,&bounds );
4328 
4329     for ( i=0; i<gd->stemcnt; ++i ) {
4330 	stem = &gd->stems[i];
4331 	hv = IsUnitHV( &stem->unit,true );
4332 	if ( !hv )
4333     continue;
4334 	if ( stem->toobig ) {
4335 	    if ( stem->left.x == bounds.minx && stem->right.x == bounds.maxx )
4336 		vstem = stem;
4337 	    else if ( stem->right.y == bounds.miny && stem->left.y == bounds.maxy )
4338 		hstem = stem;
4339     continue;
4340 	}
4341 	if ( hv == 1 ) {
4342 	    if ( stem->bbox ) hstem = stem;
4343 	    else ++hcnt;
4344 	} else if ( hv == 2 ) {
4345 	    if ( stem->bbox ) vstem = stem;
4346 	    else ++vcnt;
4347 	}
4348     }
4349     if ( hcnt!=0 && vcnt!=0 &&
4350 	( hstem == NULL || !hstem->positioned ) &&
4351 	( vstem == NULL || !vstem->positioned ))
4352 return;
4353 
4354     ch = bounds.maxy - bounds.miny;
4355     cw = bounds.maxx - bounds.minx;
4356 
4357     if ( ch > 0 && (( hstem != NULL && hstem->positioned ) ||
4358 	( hcnt == 0 && ch < gd->emsize/3 ))) {
4359 	if ( hstem != NULL && hstem->toobig ) hstem->toobig = false;
4360 	AssignPointsToBBoxHint( gd,&bounds,hstem,false );
4361 	if ( hstem != NULL ) NormalizeStem( gd,hstem );
4362     }
4363     if ( cw > 0 && (( vstem != NULL && vstem->positioned ) ||
4364 	( vcnt == 0 && cw < gd->emsize/3 ))) {
4365 	if ( vstem != NULL && vstem->toobig ) vstem->toobig = false;
4366 	AssignPointsToBBoxHint( gd,&bounds,vstem,true );
4367 	if ( vstem != NULL ) NormalizeStem( gd,vstem );
4368     }
4369 }
4370 
FindOrMakeGhostStem(struct glyphdata * gd,SplinePoint * sp,int blue,double width)4371 static struct stemdata *FindOrMakeGhostStem( struct glyphdata *gd,
4372     SplinePoint *sp,int blue,double width ) {
4373     int i, j, hasl, hasr;
4374     struct stemdata *stem=NULL, *tstem;
4375     struct stem_chunk *chunk;
4376     BasePoint dir,left,right;
4377     double min, max;
4378 
4379     dir.x = 1; dir.y = 0;
4380     for ( i=0; i<gd->stemcnt; ++i ) {
4381 	tstem = &gd->stems[i];
4382 	if ( tstem->blue == blue && tstem->ghost && tstem->width == width ) {
4383 	    stem = tstem;
4384     break;
4385 	/* If the stem controlling this blue zone is not for a ghost hint,    */
4386 	/* then we check if it has both left and right points, to ensure that */
4387 	/* we don't occasionally assign an additional point to a stem which   */
4388 	/* has already been rejected in favor of another stem */
4389 	} else if ( tstem->blue == blue && !tstem->ghost && !tstem->toobig ) {
4390 	    min = ( width == 20 ) ? tstem->left.y - tstem->lmin - 2*dist_error_hv :
4391 				    tstem->right.y - tstem->rmin - 2*dist_error_hv;
4392 	    max = ( width == 20 ) ? tstem->left.y - tstem->lmax + 2*dist_error_hv :
4393 				    tstem->right.y - tstem->rmax + 2*dist_error_hv;
4394 
4395 	    if ( sp->me.y <= min || sp->me.y >= max )
4396     continue;
4397 
4398 	    hasl = false; hasr = false; j = 0;
4399 	    while ( j < tstem->chunk_cnt && ( !hasl || !hasr )) {
4400 		chunk = &tstem->chunks[j];
4401 		if ( chunk->l != NULL && !chunk->lpotential )
4402 		    hasl = true;
4403 		if ( chunk->r != NULL && !chunk->rpotential )
4404 		    hasr = true;
4405 		j++;
4406 	    }
4407 	    if ( hasl && hasr ) {
4408 		stem = tstem;
4409     break;
4410 	    }
4411 	}
4412     }
4413 
4414     if ( stem == NULL ) {
4415 	left.x = right.x = sp->me.x;
4416 	left.y = ( width == 21 ) ? sp->me.y + 21 : sp->me.y;
4417 	right.y = ( width == 21 ) ? sp->me.y : sp->me.y - 20;
4418 
4419 	stem = NewStem( gd,&dir,&left,&right );
4420 	stem->ghost = true;
4421 	stem->width = width;
4422 	stem->blue = blue;
4423     }
4424 return( stem );
4425 }
4426 
AddGhostSegment(struct pointdata * pd,int cnt,double base,struct segment * space)4427 static int AddGhostSegment( struct pointdata *pd,int cnt,double base,struct segment *space ) {
4428     double s, e, temp, pos, spos, epos;
4429     SplinePoint *sp, *nsp, *nsp2, *psp, *psp2;
4430 
4431     sp = nsp = psp = pd->sp;
4432     pos = pd->sp->me.y;
4433 
4434     /* First check if there are points on the same line lying further */
4435     /* in the desired direction */
4436     if (( sp->next != NULL ) && ( sp->next->to->me.y == pos ))
4437 	nsp = sp->next->to;
4438     if (( sp->prev != NULL ) && ( sp->prev->from->me.y == pos ))
4439 	psp = sp->prev->from;
4440 
4441     if ( psp != sp ) {
4442 	s = psp->me.x;
4443     } else if ( psp->noprevcp ) {
4444 	psp2 = psp->prev->from;
4445 	if ( psp2->me.y != psp->me.y ) {
4446 	    s = ( psp->me.x - psp2->me.x )/( psp->me.y - psp2->me.y )*20.0;
4447 	    if ( s < 0 ) s = -s;
4448 	    if ( psp2->me.x<psp->me.x )
4449 		s = ( psp->me.x-psp2->me.x < s ) ? psp2->me.x : psp->me.x-s;
4450 	    else
4451 		s = ( psp2->me.x-psp->me.x < s ) ? psp2->me.x : psp->me.x+s;
4452 	} else
4453 	    s = psp->me.x;
4454     } else {
4455 	s = ( pd->sp->me.x + psp->prevcp.x )/2;
4456     }
4457 
4458     if ( nsp != sp ) {
4459 	e = nsp->me.x;
4460     } else if ( nsp->nonextcp ) {
4461 	nsp2 = nsp->next->to;
4462 	if ( nsp2->me.y != nsp->me.y ) {
4463 	    e = ( nsp->me.x - nsp2->me.x )/( nsp->me.y - nsp2->me.y )*20.0;
4464 	    if ( e < 0 ) e = -e;
4465 	    if ( nsp2->me.x<nsp->me.x )
4466 		e = ( nsp->me.x-nsp2->me.x < e ) ? nsp2->me.x : nsp->me.x-e;
4467 	    else
4468 		e = ( nsp2->me.x-nsp->me.x < e )  ? nsp2->me.x : nsp->me.x+e;
4469 	} else
4470 	    e = nsp->me.x;
4471     } else {
4472 	e = ( pd->sp->me.x + nsp->nextcp.x )/2;
4473     }
4474 
4475     spos = psp->me.x; epos = nsp->me.x;
4476     if ( s>e ) {
4477 	temp = s; s = e; e = temp;
4478 	temp = spos; spos = epos; epos = temp;
4479     }
4480 
4481     space[cnt].start = s - base;
4482     space[cnt].end = e - base;
4483     space[cnt].sbase = spos - base;
4484     space[cnt].ebase = epos - base;
4485     space[cnt].ecurved = space[cnt].scurved = space[cnt].curved = ( false );
4486 
4487     return( cnt+1 );
4488 }
4489 
FigureGhostActive(struct glyphdata * gd,struct stemdata * stem)4490 static void FigureGhostActive( struct glyphdata *gd,struct stemdata *stem ) {
4491     int acnt, i;
4492     real len = 0;
4493     struct segment *activespace = gd->activespace;
4494     struct pointdata *valid;
4495 
4496     if ( !stem->ghost )
4497 return;
4498 
4499     acnt = 0;
4500     for ( i=0; i<stem->chunk_cnt; ++i ) {
4501 	valid = ( stem->chunks[i].l != NULL) ?
4502 	    stem->chunks[i].l : stem->chunks[i].r;
4503 	acnt = AddGhostSegment( valid,acnt,stem->left.x,activespace );
4504     }
4505     qsort(activespace,acnt,sizeof(struct segment),segment_cmp);
4506     acnt = MergeSegments( activespace,acnt );
4507     stem->activecnt = acnt;
4508     if ( acnt!=0 ) {
4509 	stem->active = malloc(acnt*sizeof(struct segment));
4510 	memcpy( stem->active,activespace,acnt*sizeof( struct segment ));
4511     }
4512 
4513     for ( i=0; i<acnt; ++i ) {
4514 	len += stem->active[i].end-stem->active[i].start;
4515     }
4516     stem->clen = stem->len = len;
4517 }
4518 
CheckForGhostHints(struct glyphdata * gd)4519 static void CheckForGhostHints( struct glyphdata *gd ) {
4520     /* PostScript doesn't allow a hint to stretch from one alignment zone to */
4521     /*  another. (Alignment zones are the things in bluevalues).  */
4522     /* Oops, I got this wrong. PS doesn't allow a hint to start in a bottom */
4523     /*  zone and stretch to a top zone. Everything in OtherBlues is a bottom */
4524     /*  zone. The baseline entry in BlueValues is also a bottom zone. Every- */
4525     /*  thing else in BlueValues is a top-zone. */
4526     /* This means */
4527     /*  that we can't define a horizontal stem hint which stretches from */
4528     /*  the baseline to the top of a capital I, or the x-height of lower i */
4529     /*  If we find any such hints we must remove them, and replace them with */
4530     /*  ghost hints. The bottom hint has height -21, and the top -20 */
4531     BlueData *bd = &gd->bd;
4532     struct stemdata *stem;
4533     struct stem_chunk *chunk;
4534     struct pointdata *pd;
4535     real base;
4536     int i, j, leftfound, rightfound, has_h, peak, fuzz;
4537 
4538     fuzz = gd->fuzz;
4539 
4540     /* look for any stems stretching from one zone to another and remove them */
4541     /*  (I used to turn them into ghost hints here, but that didn't work (for */
4542     /*  example on "E" where we don't need any ghosts from the big stem because*/
4543     /*  the narrow stems provide the hints that PS needs */
4544     /* However, there are counter-examples. in Garamond-Pro the "T" character */
4545     /*  has a horizontal stem at the top which stretches between two adjacent */
4546     /*  bluezones. Removing it is wrong. Um... Thanks Adobe */
4547     /* I misunderstood. Both of these were top-zones */
4548     for ( i=0; i<gd->stemcnt; ++i ) {
4549 	stem = &gd->stems[i];
4550 	if ( IsUnitHV( &stem->unit,true ) != 1)
4551     continue;
4552 
4553 	leftfound = rightfound = -1;
4554 	for ( j=0; j<bd->bluecnt; ++j ) {
4555 	    if ( stem->left.y>=bd->blues[j][0]-fuzz && stem->left.y<=bd->blues[j][1]+fuzz )
4556 		leftfound = j;
4557 	    else if ( stem->right.y>=bd->blues[j][0]-fuzz && stem->right.y<=bd->blues[j][1]+fuzz )
4558 		rightfound = j;
4559 	}
4560 	/* Assign value 2 to indicate this stem should be ignored also for TTF instrs */
4561 	if ( leftfound !=-1 && rightfound !=-1 &&
4562 	    ( stem->left.y > 0 && stem->right.y <= 0 ))
4563 	    stem->toobig = 2;
4564 	/* Otherwise mark the stem as controlling a specific blue zone */
4565 	else if ( leftfound != -1 && ( rightfound == -1 || stem->left.y > 0 ))
4566 	    stem->blue = leftfound;
4567 	else if ( rightfound != -1 && ( leftfound == -1 || stem->right.y <= 0 ))
4568 	    stem->blue = rightfound;
4569     }
4570 
4571     /* Now look and see if we can find any edges which lie in */
4572     /*  these zones.  Edges which are not currently in hints */
4573     /* Use the winding number to determine top or bottom */
4574     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
4575 	has_h = false;
4576 	for ( j=0; j<gd->points[i].prevcnt; j++ ) {
4577 	    stem = gd->points[i].prevstems[j];
4578 	    if ( !stem->toobig && IsUnitHV( &stem->unit,true ) == 1 ) {
4579 		has_h = true;
4580 	break;
4581 	    }
4582 	}
4583 	for ( j=0; j<gd->points[i].nextcnt; j++ ) {
4584 	    stem = gd->points[i].nextstems[j];
4585 	    if ( !stem->toobig && IsUnitHV( &stem->unit,true ) == 1 ) {
4586 		has_h = true;
4587 	break;
4588 	    }
4589 	}
4590 	if ( has_h )
4591     continue;
4592 
4593 	pd = &gd->points[i];
4594 	base = pd->sp->me.y;
4595 	for ( j=0; j<bd->bluecnt; ++j ) {
4596 	    if ( base>=bd->blues[j][0]-fuzz && base<=bd->blues[j][1]+fuzz ) {
4597 		peak = IsSplinePeak( gd,pd,false,false,7 );
4598 		if ( peak > 0 ) {
4599 		    stem = FindOrMakeGhostStem( gd,pd->sp,j,20 );
4600 		    chunk = AddToStem( gd,stem,pd,NULL,2,false,false );
4601 		} else if ( peak < 0 ) {
4602 		    stem = FindOrMakeGhostStem( gd,pd->sp,j,21 );
4603 		    chunk = AddToStem( gd,stem,NULL,pd,2,false,false );
4604 		}
4605 	    }
4606 	}
4607     }
4608 
4609     for ( i=0; i<gd->stemcnt; ++i ) {
4610 	stem = &gd->stems[i];
4611 	if ( !stem->ghost )
4612     continue;
4613 	NormalizeStem( gd,stem );
4614 	FigureGhostActive( gd,stem );
4615     }
4616 }
4617 
MarkDStemCorner(struct glyphdata * gd,struct pointdata * pd)4618 static void MarkDStemCorner( struct glyphdata *gd,struct pointdata *pd ) {
4619     int x_dir = pd->x_corner;
4620     int hv, is_l, i, peak, has_stem = false;
4621     struct stemdata *stem;
4622     BasePoint left,right,unit;
4623 
4624     for ( i=0; i<pd->prevcnt && !has_stem; i++ ) {
4625 	stem = pd->prevstems[i];
4626 	hv = IsUnitHV( &stem->unit,true );
4627 	if ( !stem->toobig && (
4628 	    ( x_dir &&  hv == 1 ) ||
4629 	    ( !x_dir && hv == 2 )))
4630 	    has_stem = true;
4631     }
4632     for ( i=0; i<pd->nextcnt && !has_stem; i++ ) {
4633 	stem = pd->nextstems[i];
4634 	hv = IsUnitHV( &stem->unit,true );
4635 	if ( !stem->toobig && (
4636 	    ( x_dir &&  hv == 1 ) ||
4637 	    ( !x_dir && hv == 2 )))
4638 	    has_stem = true;
4639     }
4640     if ( has_stem )
4641 return;
4642 
4643     peak = IsSplinePeak( gd,pd,x_dir,x_dir,2 );
4644     unit.x = !x_dir; unit.y = x_dir;
4645 
4646     if ( peak > 0 ) {
4647 	left.x = x_dir ? pd->sp->me.x + 21 : pd->sp->me.x;
4648 	right.x = x_dir ? pd->sp->me.x : pd->sp->me.x;
4649 	left.y = x_dir ? pd->sp->me.y : pd->sp->me.y;
4650 	right.y = x_dir ? pd->sp->me.y : pd->sp->me.y - 20;
4651 
4652     } else if ( peak < 0 ) {
4653 	left.x = x_dir ? pd->sp->me.x : pd->sp->me.x;
4654 	right.x = x_dir ? pd->sp->me.x - 20 : pd->sp->me.x;
4655 	left.y = x_dir ? pd->sp->me.y : pd->sp->me.y + 21;
4656 	right.y = x_dir ? pd->sp->me.y : pd->sp->me.y;
4657     }
4658     is_l = IsCorrectSide( gd,pd,true,true,&unit );
4659     for ( i=0; i<gd->stemcnt; i++ ) {
4660 	stem = &gd->stems[i];
4661 	if (!stem->toobig && UnitsParallel( &unit,&stem->unit,true ) &&
4662 	    OnStem( stem,&pd->sp->me,is_l ))
4663     break;
4664     }
4665     if ( i == gd->stemcnt ) {
4666 	stem = NewStem( gd,&unit,&left,&right );
4667 	stem->ghost = 2;
4668     }
4669     AddToStem( gd,stem,pd,NULL,2,false,false );
4670 }
4671 
MarkDStemCorners(struct glyphdata * gd)4672 static void MarkDStemCorners( struct glyphdata *gd ) {
4673     struct stemdata *stem;
4674     struct stem_chunk *schunk, *echunk;
4675     int i;
4676 
4677     for ( i=0; i<gd->stemcnt; ++i ) {
4678 	stem = &gd->stems[i];
4679 	if ( stem->toobig || IsUnitHV( &stem->unit,true ))
4680     continue;
4681 
4682 	schunk = &stem->chunks[0];
4683 	echunk = &stem->chunks[stem->chunk_cnt - 1];
4684 
4685 	if ( schunk->l != NULL && schunk->r != NULL &&
4686 	    fabs( schunk->l->base.x - schunk->r->base.x ) > dist_error_hv &&
4687             fabs( schunk->l->base.y - schunk->r->base.y ) > dist_error_hv && (
4688 	    ( schunk->l->x_corner == 1 && schunk->r->y_corner == 1 ) ||
4689 	    ( schunk->l->y_corner == 1 && schunk->r->x_corner == 1 ))) {
4690 	    MarkDStemCorner( gd,schunk->l );
4691 	    MarkDStemCorner( gd,schunk->r );
4692 	}
4693 	if ( echunk->l != NULL && echunk->r != NULL &&
4694 	    fabs( echunk->l->base.x - echunk->r->base.x ) > dist_error_hv &&
4695             fabs( echunk->l->base.y - echunk->r->base.y ) > dist_error_hv && (
4696 	    ( echunk->l->x_corner == 1 && echunk->r->y_corner == 1 ) ||
4697 	    ( echunk->l->y_corner == 1 && echunk->r->x_corner == 1 ))) {
4698 	    MarkDStemCorner( gd,echunk->l );
4699 	    MarkDStemCorner( gd,echunk->r );
4700 	}
4701     }
4702 }
4703 
4704 #if GLYPH_DATA_DEBUG
DumpGlyphData(struct glyphdata * gd)4705 static void DumpGlyphData( struct glyphdata *gd ) {
4706     int i, j;
4707     struct stemdata *stem;
4708     struct linedata *line;
4709     struct stem_chunk *chunk;
4710 
4711     if ( gd->linecnt > 0 )
4712 	fprintf( stderr, "\nDumping line data for %s\n",gd->sc->name );
4713     for ( i=0; i<gd->linecnt; ++i ) {
4714 	line = &gd->lines[i];
4715 	fprintf( stderr, "line vector=%.4f,%.4f base=%.2f,%.2f length=%.4f\n",
4716 	    line->unit.x,line->unit.y,line->online.x,line->online.y,line->length );
4717 	for( j=0; j<line->pcnt;++j ) {
4718 	    fprintf( stderr, "\tpoint num=%d, x=%.2f, y=%.2f, prev=%d, next=%d\n",
4719 		line->points[j]->sp->ttfindex, line->points[j]->sp->me.x,
4720 		line->points[j]->sp->me.y,
4721 		line->points[j]->prevline==line, line->points[j]->nextline==line );
4722 	}
4723 	fprintf( stderr, "\n" );
4724     }
4725 
4726     if ( gd->stemcnt > 0 )
4727 	fprintf( stderr, "\nDumping stem data for %s\n",gd->sc->name );
4728     for ( i=0; i<gd->stemcnt; ++i ) {
4729 	stem = &gd->stems[i];
4730 	fprintf( stderr, "stem l=%.2f,%.2f idx=%d r=%.2f,%.2f idx=%d vector=%.4f,%.4f\n\twidth=%.2f chunk_cnt=%d len=%.4f clen=%.4f ghost=%d blue=%d toobig=%d\n\tlmin=%.2f,lmax=%.2f,rmin=%.2f,rmax=%.2f,lpcnt=%d,rpcnt=%d\n",
4731 	    stem->left.x,stem->left.y,stem->leftidx,
4732 	    stem->right.x,stem->right.y,stem->rightidx,
4733 	    stem->unit.x,stem->unit.y,stem->width,
4734 	    stem->chunk_cnt,stem->len,stem->clen,stem->ghost,stem->blue,stem->toobig,
4735 	    stem->lmin,stem->lmax,stem->rmin,stem->rmax,stem->lpcnt,stem->rpcnt );
4736 	for ( j=0; j<stem->chunk_cnt; ++j ) {
4737 	    chunk = &stem->chunks[j];
4738 	    if ( chunk->l!=NULL && chunk->r!=NULL )
4739 		fprintf (stderr, "\tchunk l=%.2f,%.2f potential=%d r=%.2f,%.2f potential=%d stub=%d\n",
4740 		    chunk->l->sp->me.x, chunk->l->sp->me.y, chunk->lpotential,
4741 		    chunk->r->sp->me.x, chunk->r->sp->me.y, chunk->rpotential, chunk->stub );
4742 	    else if ( chunk->l!=NULL )
4743 		fprintf (stderr, "\tchunk l=%.2f,%.2f potential=%d\n",
4744 		    chunk->l->sp->me.x, chunk->l->sp->me.y, chunk->lpotential);
4745 	    else if ( chunk->r!=NULL )
4746 		fprintf (stderr, "\tchunk r=%.2f,%.2f potential=%d\n",
4747 		    chunk->r->sp->me.x, chunk->r->sp->me.y, chunk->rpotential);
4748 	}
4749 	fprintf( stderr, "\n" );
4750     }
4751 
4752     if ( gd->hbundle != NULL || gd->vbundle != NULL )
4753 	fprintf( stderr, "\nDumping HV stem bundles for %s\n",gd->sc->name );
4754     if ( gd->hbundle != NULL ) for ( i=0; i<gd->hbundle->cnt; i++ ) {
4755 	stem = gd->hbundle->stemlist[i];
4756 	fprintf( stderr, "H stem l=%.2f,%.2f r=%.2f,%.2f slave=%d\n",
4757 	    stem->left.x,stem->left.y,stem->right.x,stem->right.y,stem->master!=NULL );
4758 	if ( stem->dep_cnt > 0 ) for ( j=0; j<stem->dep_cnt; j++ ) {
4759 	    fprintf( stderr, "\tslave l=%.2f,%.2f r=%.2f,%.2f mode=%c left=%d\n",
4760 		stem->dependent[j].stem->left.x,stem->dependent[j].stem->left.y,
4761 		stem->dependent[j].stem->right.x,stem->dependent[j].stem->right.y,
4762 		stem->dependent[j].dep_type,stem->dependent[j].lbase );
4763 	}
4764 	if ( stem->serif_cnt > 0 ) for ( j=0; j<stem->serif_cnt; j++ ) {
4765 	    fprintf( stderr, "\tserif l=%.2f,%.2f r=%.2f,%.2f ball=%d left=%d\n",
4766 		stem->serifs[j].stem->left.x,stem->serifs[j].stem->left.y,
4767 		stem->serifs[j].stem->right.x,stem->serifs[j].stem->right.y,
4768 		stem->serifs[j].is_ball,stem->serifs[j].lbase );
4769 	}
4770     }
4771     fprintf( stderr, "\n" );
4772     if ( gd->vbundle != NULL ) for ( i=0; i<gd->vbundle->cnt; i++ ) {
4773 	stem = gd->vbundle->stemlist[i];
4774 	fprintf( stderr, "V stem l=%.2f,%.2f r=%.2f,%.2f slave=%d\n",
4775 	    stem->left.x,stem->left.y,stem->right.x,stem->right.y,stem->master!=NULL );
4776 	if ( stem->dep_cnt > 0 ) for ( j=0; j<stem->dep_cnt; j++ ) {
4777 	    fprintf( stderr, "\tslave l=%.2f,%.2f r=%.2f,%.2f mode=%c left=%d\n",
4778 		stem->dependent[j].stem->left.x,stem->dependent[j].stem->left.y,
4779 		stem->dependent[j].stem->right.x,stem->dependent[j].stem->right.y,
4780 		stem->dependent[j].dep_type,stem->dependent[j].lbase );
4781 	}
4782 	if ( stem->serif_cnt > 0 ) for ( j=0; j<stem->serif_cnt; j++ ) {
4783 	    fprintf( stderr, "\tserif l=%.2f,%.2f r=%.2f,%.2f ball=%d left=%d\n",
4784 		stem->serifs[j].stem->left.x,stem->serifs[j].stem->left.y,
4785 		stem->serifs[j].stem->right.x,stem->serifs[j].stem->right.y,
4786 		stem->serifs[j].is_ball,stem->serifs[j].lbase );
4787 	}
4788 	if ( stem->prev_c_m != NULL ) {
4789 	    fprintf( stderr,"\tprev counter master: l=%.2f r=%.2f\n",
4790 		stem->prev_c_m->left.x,stem->prev_c_m->right.x );
4791 	}
4792 	if ( stem->next_c_m != NULL ) {
4793 	    fprintf( stderr,"\tnext counter master: l=%.2f r=%.2f\n",
4794 		stem->next_c_m->left.x,stem->next_c_m->right.x );
4795 	}
4796     }
4797     fprintf( stderr, "\n" );
4798 
4799     if ( gd->ibundle != NULL ) for ( i=0; i<gd->ibundle->cnt; i++ ) {
4800 	stem = gd->ibundle->stemlist[i];
4801 	fprintf( stderr, "I stem l=%.2f,%.2f r=%.2f,%.2f slave=%d\n",
4802 	    stem->left.x,stem->left.y,stem->right.x,stem->right.y,stem->master!=NULL );
4803 	if ( stem->dep_cnt > 0 ) for ( j=0; j<stem->dep_cnt; j++ ) {
4804 	    fprintf( stderr, "\tslave l=%.2f,%.2f r=%.2f,%.2f mode=%c left=%d\n",
4805 		stem->dependent[j].stem->left.x,stem->dependent[j].stem->left.y,
4806 		stem->dependent[j].stem->right.x,stem->dependent[j].stem->right.y,
4807 		stem->dependent[j].dep_type,stem->dependent[j].lbase );
4808 	}
4809 	if ( stem->serif_cnt > 0 ) for ( j=0; j<stem->serif_cnt; j++ ) {
4810 	    fprintf( stderr, "\tserif l=%.2f,%.2f r=%.2f,%.2f ball=%d left=%d\n",
4811 		stem->serifs[j].stem->left.x,stem->serifs[j].stem->left.y,
4812 		stem->serifs[j].stem->right.x,stem->serifs[j].stem->right.y,
4813 		stem->serifs[j].is_ball,stem->serifs[j].lbase );
4814 	}
4815     }
4816     fprintf( stderr, "\n" );
4817 }
4818 #endif
4819 
AssignPointsToStems(struct glyphdata * gd,int startnum,DBounds * bounds)4820 static void AssignPointsToStems( struct glyphdata *gd,int startnum,DBounds *bounds ) {
4821     int i;
4822     struct pointdata *pd;
4823     struct stemdata *stem = NULL;
4824     BasePoint dir;
4825 
4826     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
4827 	pd = &gd->points[i];
4828 	if ( pd->prev_e_cnt > 0 )
4829 	    BuildStem( gd,pd,false,true,true,0 );
4830 	else
4831 	    HalfStemNoOpposite( gd,pd,stem,&pd->prevunit,false );
4832 
4833 	if ( pd->next_e_cnt > 0 )
4834 	    BuildStem( gd,pd,true,true,true,0 );
4835 	else
4836 	    HalfStemNoOpposite( gd,pd,stem,&pd->nextunit,true );
4837 
4838 	if ( pd->x_corner ) {
4839 	    if ( pd->bothedge!=NULL )
4840 		stem = DiagonalCornerStem( gd,pd,true );
4841 	    dir.x = 0; dir.y = 1;
4842 	    HalfStemNoOpposite( gd,pd,stem,&dir,2 );
4843 	} else if ( pd->y_corner ) {
4844 	    if ( pd->bothedge!=NULL )
4845 		stem = DiagonalCornerStem( gd,pd,true );
4846 	    dir.x = 1; dir.y = 0;
4847 	    HalfStemNoOpposite( gd,pd,stem,&dir,2 );
4848 	}
4849     }
4850     gd->lspace = malloc(gd->pcnt*sizeof(struct segment));
4851     gd->rspace = malloc(gd->pcnt*sizeof(struct segment));
4852     gd->bothspace = malloc(3*gd->pcnt*sizeof(struct segment));
4853     gd->activespace = malloc(3*gd->pcnt*sizeof(struct segment));
4854 #if GLYPH_DATA_DEBUG
4855     fprintf( stderr,"Going to calculate stem active zones for %s\n",gd->sc->name );
4856 #endif
4857     for ( i=startnum; i<gd->stemcnt; ++i ) {
4858 	stem = &gd->stems[i];
4859 	NormalizeStem( gd,stem );
4860 	if ( gd->stems[i].ghost )
4861 	    FigureGhostActive( gd,stem );
4862 	else if ( gd->stems[i].bbox )
4863 	    AssignPointsToBBoxHint( gd,bounds,stem,( stem->unit.y == 1 ));
4864 	else
4865 	    FigureStemActive( gd,&gd->stems[i] );
4866     }
4867 #if GLYPH_DATA_DEBUG
4868     DumpGlyphData( gd );
4869 #endif
4870 
4871     free(gd->lspace);		gd->lspace = NULL;
4872     free(gd->rspace);		gd->rspace = NULL;
4873     free(gd->bothspace);	gd->bothspace = NULL;
4874     free(gd->activespace);	gd->activespace = NULL;
4875 }
4876 
_DStemInfoToStemData(struct glyphdata * gd,DStemInfo * dsi,int * startcnt)4877 static void _DStemInfoToStemData( struct glyphdata *gd,DStemInfo *dsi,int *startcnt ) {
4878     struct stemdata *stem;
4879 
4880     if ( gd->stems == NULL ) {
4881 	gd->stems = calloc( 2*gd->pcnt,sizeof( struct stemdata ));
4882 	gd->stemcnt = 0;
4883     }
4884     *startcnt = gd->stemcnt;
4885     while ( dsi != NULL ) {
4886 	stem = NewStem( gd,&dsi->unit,&dsi->left,&dsi->right );
4887 	stem->positioned = true;
4888 	dsi = dsi->next;
4889     }
4890 }
4891 
DStemInfoToStemData(struct glyphdata * gd,DStemInfo * dsi)4892 struct glyphdata *DStemInfoToStemData( struct glyphdata *gd,DStemInfo *dsi ) {
4893     int startcnt;
4894 
4895     if ( dsi == NULL )
4896 return( gd );
4897 
4898     _DStemInfoToStemData( gd,dsi,&startcnt );
4899     AssignPointsToStems( gd,startcnt,NULL );
4900 return( gd );
4901 }
4902 
_StemInfoToStemData(struct glyphdata * gd,StemInfo * si,DBounds * bounds,int is_v,int * startcnt)4903 static void _StemInfoToStemData( struct glyphdata *gd,StemInfo *si,DBounds *bounds,int is_v,int *startcnt ) {
4904     struct stemdata *stem;
4905     BasePoint dir,left,right;
4906 
4907     dir.x = !is_v; dir.y = is_v;
4908     if ( gd->stems == NULL ) {
4909 	gd->stems = calloc( 2*gd->pcnt,sizeof( struct stemdata ));
4910 	gd->stemcnt = 0;
4911     }
4912     *startcnt = gd->stemcnt;
4913 
4914     while ( si != NULL ) {
4915 	left.x = ( is_v ) ? si->start : 0;
4916 	left.y = ( is_v ) ? 0 : si->start + si->width;
4917 	right.x = ( is_v ) ? si->start + si->width : 0;
4918 	right.y = ( is_v ) ? 0 : si->start;
4919 	stem = NewStem( gd,&dir,&left,&right );
4920 	stem->ghost = si->ghost;
4921 	if (( is_v &&
4922 		left.x >= bounds->minx && left.x < bounds->minx + dist_error_hv &&
4923 		right.x > bounds->maxx - dist_error_hv && right.x <= bounds->maxx ) ||
4924 	    ( !is_v &&
4925 		right.y >= bounds->miny && right.y < bounds->miny + dist_error_hv &&
4926 		left.y > bounds->maxy - dist_error_hv && left.y <= bounds->maxy ))
4927 	    stem->bbox = true;
4928 	stem->positioned = true;
4929 	si = si->next;
4930     }
4931 }
4932 
StemInfoToStemData(struct glyphdata * gd,StemInfo * si,int is_v)4933 struct glyphdata *StemInfoToStemData( struct glyphdata *gd,StemInfo *si,int is_v ) {
4934     DBounds bounds;
4935     int startcnt;
4936 
4937     if ( si == NULL )
4938 return( gd );
4939 
4940     SplineCharFindBounds( gd->sc,&bounds );
4941     _StemInfoToStemData( gd,si,&bounds,is_v,&startcnt );
4942 
4943     AssignPointsToStems( gd,startcnt,&bounds );
4944 return( gd );
4945 }
4946 
ValidConflictingStem(struct stemdata * stem1,struct stemdata * stem2)4947 static int ValidConflictingStem( struct stemdata *stem1,struct stemdata *stem2 ) {
4948     int x_dir = fabs( stem1->unit.y ) > fabs( stem1->unit.x );
4949     double s1, e1, s2, e2, temp;
4950 
4951     s1 = (&stem1->left.x)[!x_dir] -
4952 	((&stem1->left.x)[x_dir] * (&stem1->unit.x)[!x_dir] )/(&stem1->unit.x)[x_dir];
4953     e1 = (&stem1->right.x)[!x_dir] -
4954 	((&stem1->right.x)[x_dir] * (&stem1->unit.x)[!x_dir] )/(&stem1->unit.x)[x_dir];
4955     s2 = (&stem2->left.x)[!x_dir] -
4956 	((&stem2->left.x)[x_dir] * (&stem2->unit.x)[!x_dir] )/(&stem2->unit.x)[x_dir];
4957     e2 = (&stem2->right.x)[!x_dir] -
4958 	((&stem2->right.x)[x_dir] * (&stem2->unit.x)[!x_dir] )/(&stem2->unit.x)[x_dir];
4959 
4960     if ( s1 > e1 ) {
4961 	temp = s1; s1 = e1; e1 = temp;
4962     }
4963     if ( s2 > e2 ) {
4964 	temp = s2; s2 = e2; e2 = temp;
4965     }
4966     /* If stems don't overlap, then there is no conflict here */
4967     if ( s2 >= e1 || s1 >= e2 )
4968 return( false );
4969 
4970     /* Stems which have no points assigned cannot be valid masters for    */
4971     /* other stems (however there is a notable exception for ghost hints) */
4972     if (( stem1->lpcnt > 0 || stem1->rpcnt > 0 ) &&
4973 	stem2->lpcnt == 0 && stem2->rpcnt == 0 && !stem2->ghost )
4974 return( false );
4975 
4976     /* Bounding box stems are always preferred */
4977     if ( stem1->bbox && !stem2->bbox )
4978 return( false );
4979 
4980     /* Stems associated with blue zones always preferred to any other stems */
4981     if ( stem1->blue >=0 && stem2->blue < 0 )
4982 return( false );
4983     /* Don't attempt to handle together stems, linked to different zones */
4984     if ( stem1->blue >=0 && stem2->blue >= 0 && stem1->blue != stem2->blue )
4985 return( false );
4986     /* If both stems are associated with a blue zone, but one of them is for */
4987     /* a ghost hint, then that stem is preferred */
4988     if ( stem1->ghost && !stem2->ghost )
4989 return( false );
4990 
4991 return( true );
4992 }
4993 
HasDependentStem(struct stemdata * master,struct stemdata * slave)4994 static int HasDependentStem( struct stemdata *master,struct stemdata *slave ) {
4995     int i;
4996     struct stemdata *tstem;
4997 
4998     if ( slave->master != NULL && master->dep_cnt > 0 ) {
4999 	for ( i=0; i<master->dep_cnt; i++ ) {
5000 	    tstem = master->dependent[i].stem;
5001 	    if ( tstem == slave || HasDependentStem( tstem,slave ))
5002 return( true );
5003 	}
5004     }
5005 return( false );
5006 }
5007 
PreferEndDep(struct stemdata * stem,struct stemdata * smaster,struct stemdata * emaster,char s_type,char e_type)5008 static int PreferEndDep( struct stemdata *stem,
5009     struct stemdata *smaster,struct stemdata *emaster,char s_type,char e_type ) {
5010 
5011     int hv = IsUnitHV( &stem->unit,true );
5012     double sdist, edist;
5013 
5014     if ( !hv )
5015 return( false );
5016 
5017     if (( s_type == 'a' && e_type != 'a' ) || ( s_type == 'm' && e_type == 'i' ))
5018 return( false );
5019     else if (( e_type == 'a' && s_type != 'a' ) || ( e_type == 'm' && s_type == 'i' ))
5020 return( true );
5021 
5022     if ( s_type == 'm' && s_type == e_type ) {
5023 	sdist = ( hv==1 ) ?
5024 	    fabs( smaster->right.y - stem->right.y ) :
5025 	    fabs( smaster->left.x - stem->left.x );
5026 	edist = ( hv==1 ) ?
5027 	    fabs( emaster->left.y - stem->left.y ) :
5028 	    fabs( emaster->right.x - stem->right.x );
5029 return( edist < sdist );
5030     } else
5031 return( emaster->clen > smaster->clen );
5032 }
5033 
LookForMasterHVStem(struct stemdata * stem,BlueData * bd)5034 static void LookForMasterHVStem( struct stemdata *stem,BlueData *bd ) {
5035     struct stemdata *tstem, *smaster=NULL, *emaster=NULL;
5036     struct stembundle *bundle = stem->bundle;
5037     double start, end, tstart, tend;
5038     double ssdist, sedist, esdist, eedist;
5039     double smin, smax, emin, emax, tsmin, tsmax, temin, temax;
5040     int is_x, i, link_to_s, stype, etype, allow_s, allow_e;
5041 
5042     is_x = ( bundle->unit.x == 1 );
5043     if ( is_x ) {
5044 	start = stem->right.y; end = stem->left.y;
5045 	smin = start - stem->rmin - 2*dist_error_hv;
5046 	smax = start - stem->rmax + 2*dist_error_hv;
5047 	emin = end - stem->lmin - 2*dist_error_hv;
5048 	emax = end - stem->lmax + 2* dist_error_hv;
5049     } else {
5050 	start = stem->left.x; end = stem->right.x;
5051 	smin = start + stem->lmax - 2*dist_error_hv;
5052 	smax = start + stem->lmin + 2*dist_error_hv;
5053 	emin = end + stem->rmax - 2*dist_error_hv;
5054 	emax = end + stem->rmin + 2*dist_error_hv;
5055     }
5056     start = ( is_x ) ? stem->right.y : stem->left.x;
5057     end = ( is_x ) ? stem->left.y : stem->right.x;
5058     stype = etype = '\0';
5059 
5060     for ( i=0; i<bundle->cnt; i++ ) {
5061 	tstem = bundle->stemlist[i];
5062 	if ( is_x ) {
5063 	    tstart = tstem->right.y; tend = tstem->left.y;
5064 	    tsmin = tstart - tstem->rmin - 2*dist_error_hv;
5065 	    tsmax = tstart - tstem->rmax + 2*dist_error_hv;
5066 	    temin = tend - tstem->lmin - 2*dist_error_hv;
5067 	    temax = tend - tstem->lmax + 2* dist_error_hv;
5068 	} else {
5069 	    tstart = tstem->left.x; tend = tstem->right.x;
5070 	    tsmin = tstart + tstem->lmax - 2*dist_error_hv;
5071 	    tsmax = tstart + tstem->lmin + 2*dist_error_hv;
5072 	    temin = tend + tstem->rmax - 2*dist_error_hv;
5073 	    temax = tend + tstem->rmin + 2*dist_error_hv;
5074 	}
5075 	tstart = ( is_x ) ? tstem->right.y : tstem->left.x;
5076 	tend = ( is_x ) ? tstem->left.y : tstem->right.x;
5077 
5078 	/* In this loop we are looking if the given stem has conflicts with */
5079 	/* other stems and if anyone of those conflicting stems should      */
5080 	/* take precedence over it */
5081 	if ( stem == tstem || tend < start || tstart > end ||
5082 	    !ValidConflictingStem( stem,tstem ) || HasDependentStem( stem,tstem ))
5083     continue;
5084 	/* Usually in case of conflicts we prefer the stem with longer active */
5085 	/* zones. However a stem linked to a blue zone is always preferred to */
5086 	/* a stem which is not, and ghost hints are preferred to any other    */
5087 	/* stems */
5088 	if ( stem->clen > tstem->clen && ValidConflictingStem( tstem,stem ))
5089     continue;
5090 
5091 	stem->confl_cnt++;
5092 
5093 	/* If the master stem is for a ghost hint or both the stems are    */
5094 	/* linked to the same blue zone, then we can link only to the edge */
5095 	/* which fall into the blue zone */
5096 	allow_s = ( !tstem->ghost || tstem->width == 21 ) &&
5097 	    ( stem->blue == -1 || stem->blue != tstem->blue || bd->blues[stem->blue][0] < 0 );
5098 	allow_e = ( !tstem->ghost || tstem->width == 20 ) &&
5099 	    ( stem->blue == -1 || stem->blue != tstem->blue || bd->blues[stem->blue][0] > 0 );
5100 
5101 	/* Assume there are two stems which have (almost) coincident left edges. */
5102 	/* The hinting technique for this case is to merge all points found on   */
5103 	/* those coincident edges together, position them, and then link to the  */
5104 	/* opposite edges. */
5105 	/* However we don't allow merging if both stems can be snapped to a blue    */
5106 	/* zone, unless their edges are _exactly_ coincident, as shifting features  */
5107 	/* relatively to each other instead of snapping them to the same zone would */
5108 	/* obviously be wrong */
5109 	if ( allow_s && tstart > smin && tstart < smax && start > tsmin && start < tsmax &&
5110 	    ( stem->blue == -1 || RealNear( tstart,start ))) {
5111 
5112 	    if ( smaster == NULL || stype != 'a' || smaster->clen < tstem->clen ) {
5113 		smaster = tstem;
5114 		stype = 'a';
5115 	    }
5116 	/* The same case for right edges */
5117 	} else if ( allow_e && tend > emin && tend < emax && end > temin && end < temax &&
5118 	    ( stem->blue == -1 || RealNear( tend,end ))) {
5119 
5120 	    if ( emaster == NULL || etype != 'a' || emaster->clen < tstem->clen ) {
5121 		emaster = tstem;
5122 		etype = 'a';
5123 	    }
5124 
5125 	/* Nested stems. I first planned to handle them by positioning the      */
5126 	/* narrower stem first, and then linking its edges to the opposed edges */
5127 	/* of the nesting stem. But this works well only in those cases where   */
5128 	/* maintaining the dependent stem width is not important. So now the    */
5129 	/* situations where a narrower or a wider stem can be preferred         */
5130 	/* (because it has longer active zones) are equally possible. In the    */
5131 	/* first case I link to the master stem just one edge of the secondary  */
5132 	/* stem, just like with overlapping stems */
5133 	} else if ( tstart > start && tend < end ) {
5134 	    if ( allow_s && ( smaster == NULL || stype == 'i' ||
5135 		( stype == 'm' && smaster->clen < tstem->clen ))) {
5136 
5137 		smaster = tstem;
5138 		stype = 'm';
5139 	    }
5140 	    if ( allow_e && ( emaster == NULL || etype == 'i' ||
5141 		( etype == 'm' && emaster->clen < tstem->clen ))) {
5142 
5143 		emaster = tstem;
5144 		etype = 'm';
5145 	    }
5146 	/* However if we have to prefer the nesting stem, we do as with      */
5147 	/* overlapping stems which require interpolations, i. e. interpolate */
5148 	/* one edge and link to another */
5149 	} else if ( tstart < start && tend > end ) {
5150 	    link_to_s = ( allow_s && ( start - tstart < tend - end ));
5151 	    if ( link_to_s && ( smaster == NULL ||
5152 		( stype == 'i' && smaster->clen < tstem->clen ))) {
5153 		smaster = tstem;
5154 		stype = 'i';
5155 	    } else if ( !link_to_s && ( emaster == NULL ||
5156 		( etype == 'i' && emaster->clen < tstem->clen ))) {
5157 		emaster = tstem;
5158 		etype = 'i';
5159 	    }
5160 	/* Overlapping stems. Here we first check all 4 distances between */
5161 	/* 4 stem edges. If the closest distance is between left or right */
5162 	/* edges, then the normal technique (in TrueType) is linking them */
5163 	/* with MDRP without maintaining a minimum distance. Otherwise    */
5164 	/* we interpolate an edge of the "slave" stem between already     */
5165 	/* positioned edges of the "master" stem, and then gridfit it     */
5166 	} else if (( tstart < start && start < tend && tend < end ) ||
5167 	    ( start < tstart && tstart < end && end < tend )) {
5168 
5169 	    ssdist = fabs( start - tstart );
5170 	    sedist = fabs( start - tend );
5171 	    esdist = fabs( end - tstart );
5172 	    eedist = fabs( end - tend );
5173 
5174 	    if ( allow_s && ( !allow_e ||
5175 		( stem->width < tstem->width/3 && ssdist < eedist ) ||
5176 		( ssdist <= eedist && ssdist <= sedist && ssdist <= esdist )) &&
5177 		( smaster == NULL || ( stype == 'i' ||
5178 		( stype == 'm' && smaster->clen < tstem->clen )))) {
5179 
5180 		smaster = tstem;
5181 		stype = 'm';
5182 	    } else if ( allow_e && ( !allow_s ||
5183 		( stem->width < tstem->width/3 && eedist < ssdist ) ||
5184 		( eedist <= ssdist && eedist <= sedist && eedist <= esdist )) &&
5185 		( emaster == NULL || ( etype == 'i' ||
5186 		( etype == 'm' && emaster->clen < tstem->clen )))) {
5187 
5188 		emaster = tstem;
5189 		etype = 'm';
5190 	    } else if ( allow_s && allow_e && ( smaster == NULL ||
5191 		( stype == 'i' && smaster->clen < tstem->clen )) &&
5192 		sedist <= esdist && sedist <= ssdist && sedist <= eedist ) {
5193 
5194 		smaster = tstem;
5195 		stype = 'i';
5196 	    } else if ( allow_s && allow_e && ( emaster == NULL ||
5197 		( etype == 'i' && emaster->clen < tstem->clen )) &&
5198 		esdist <= sedist && esdist <= ssdist && esdist <= eedist ) {
5199 
5200 		emaster = tstem;
5201 		etype = 'i';
5202 	    }
5203 	}
5204     }
5205     if ( smaster != NULL && emaster != NULL ) {
5206 	if ( PreferEndDep( stem,smaster,emaster,stype,etype ))
5207 	    smaster = NULL;
5208 	else
5209 	    emaster = NULL;
5210     }
5211 
5212     if ( smaster != NULL ) {
5213 	stem->master = smaster;
5214 	if ( smaster->dependent == NULL )
5215 	    smaster->dependent = calloc( bundle->cnt*2,sizeof( struct dependent_stem ));
5216 	smaster->dependent[smaster->dep_cnt].stem = stem;
5217 	smaster->dependent[smaster->dep_cnt].dep_type = stype;
5218 	smaster->dependent[smaster->dep_cnt++].lbase = !is_x;
5219     } else if ( emaster != NULL ) {
5220 	stem->master = emaster;
5221 	if ( emaster->dependent == NULL )
5222 	    emaster->dependent = calloc( bundle->cnt*2,sizeof( struct dependent_stem ));
5223 	emaster->dependent[emaster->dep_cnt  ].stem = stem;
5224 	emaster->dependent[emaster->dep_cnt  ].dep_type = etype;
5225 	emaster->dependent[emaster->dep_cnt++].lbase = is_x;
5226     }
5227 }
5228 
5229 /* If a stem has been considered depending from another stem which in   */
5230 /* its turn has its own "master", and the first stem doesn't conflict   */
5231 /* with the "master" of the stem it overlaps (or any other stems), then */
5232 /* this dependency is unneeded and processing it in the autoinstructor  */
5233 /* can even lead to undesired effects. Unfortunately we can't prevent   */
5234 /* detecting such dependecies in LookForMasterHVStem(), because we      */
5235 /* need to know the whole stem hierarchy first. So look for undesired   */
5236 /* dependencies and clean them now */
ClearUnneededDeps(struct stemdata * stem)5237 static void ClearUnneededDeps( struct stemdata *stem ) {
5238     struct stemdata *master;
5239     int i, j;
5240 
5241     if ( stem->confl_cnt == 1 &&
5242 	( master = stem->master ) != NULL && master->master != NULL ) {
5243 
5244 	stem->master = NULL;
5245 	for ( i=j=0; i<master->dep_cnt; i++ ) {
5246 	    if ( j<i )
5247 		memcpy( &master->dependent[i-1],&master->dependent[i],
5248 		    sizeof( struct dependent_stem ));
5249 	    if ( master->dependent[i].stem != stem ) j++;
5250 	}
5251 	(master->dep_cnt)--;
5252     }
5253 }
5254 
GDBundleStems(struct glyphdata * gd,int maxtoobig,int needs_deps)5255 static void GDBundleStems( struct glyphdata *gd, int maxtoobig, int needs_deps ) {
5256     struct stemdata *stem, *tstem;
5257     int i, j, k, hv, hasl, hasr, stem_cnt;
5258     struct pointdata *lpd, *rpd;
5259     double dmove;
5260     DBounds bounds;
5261 
5262     /* Some checks for undesired stems which we couldn't do earlier  */
5263 
5264     /* First filter out HV stems which have only "potential" points  */
5265     /* on their left or right edge. Such stems aren't supposed to be */
5266     /* used for PS hinting, so we mark them as "too big" */
5267     for ( i=0; i<gd->stemcnt; ++i ) {
5268 	stem = &gd->stems[i];
5269 	hasl = false; hasr = false;
5270 
5271 	if ( IsUnitHV( &stem->unit,true ) &&
5272 	    !stem->toobig && !stem->ghost && !stem->positioned ) {
5273 	    for ( j=0; j<stem->chunk_cnt && ( !hasl || !hasr ); ++j ) {
5274 		if ( stem->chunks[j].l!=NULL && !stem->chunks[j].lpotential )
5275 		    hasl = true;
5276 		if ( stem->chunks[j].r!=NULL && !stem->chunks[j].rpotential )
5277 		    hasr = true;
5278 	    }
5279 	    if ( !hasl || !hasr )
5280 		stem->toobig = true;
5281 	}
5282     }
5283 
5284     /* Filter out HV stems which have both their edges controlled by */
5285     /* other, narrower HV stems */
5286     for ( i=0; i<gd->stemcnt; ++i ) {
5287 	stem = &gd->stems[i];
5288 	hv = IsUnitHV( &stem->unit,true );
5289 
5290 	if ( IsUnitHV( &stem->unit,true )) {
5291 	    hasl = hasr = false;
5292 	    for ( j=0; j<stem->chunk_cnt; ++j ) {
5293 		lpd = stem->chunks[j].l;
5294 		rpd = stem->chunks[j].r;
5295 		if ( lpd != NULL ) {
5296 		    stem_cnt = ( stem->chunks[j].lnext ) ? lpd->nextcnt : lpd->prevcnt;
5297 		    for ( k=0; k<stem_cnt; k++ ) {
5298 			tstem = ( stem->chunks[j].lnext ) ?
5299 			    lpd->nextstems[k] : lpd->prevstems[k];
5300 			/* Used to test tstem->toobig <= stem->toobig, but got into troubles with */
5301 			/* a weird terminal stem preventing a ball terminal from being properly detected, */
5302 			/* because both the stems initially have toobig == 1. */
5303 			/* See the "f" from Heuristica-Italic */
5304 			if ( tstem != stem &&
5305 			    !tstem->toobig && tstem->positioned >= stem->positioned &&
5306 			    tstem->width < stem->width && hv == IsUnitHV( &tstem->unit,true )) {
5307 			    hasl = true;
5308 		    break;
5309 			}
5310 		    }
5311 		}
5312 		if ( rpd != NULL ) {
5313 		    stem_cnt = ( stem->chunks[j].rnext ) ? rpd->nextcnt : rpd->prevcnt;
5314 		    for ( k=0; k<stem_cnt; k++ ) {
5315 			tstem = ( stem->chunks[j].rnext ) ?
5316 			    rpd->nextstems[k] : rpd->prevstems[k];
5317 			if ( tstem != stem &&
5318 			    !tstem->toobig && tstem->positioned >= stem->positioned &&
5319 			    tstem->width < stem->width && hv == IsUnitHV( &tstem->unit,true )) {
5320 			    hasr = true;
5321 		    break;
5322 			}
5323 		    }
5324 		}
5325 		if ( hasl && hasr ) {
5326 		    stem->toobig = 2;
5327 	    break;
5328 		}
5329 	    }
5330 	}
5331     }
5332 
5333     gd->hbundle = calloc( 1,sizeof( struct stembundle ));
5334     gd->hbundle->stemlist = calloc( gd->stemcnt,sizeof( struct stemdata *));
5335     gd->hbundle->unit.x = 1; gd->hbundle->unit.y = 0;
5336     gd->hbundle->l_to_r.x = 0; gd->hbundle->l_to_r.y = -1;
5337 
5338     gd->vbundle = calloc( 1,sizeof( struct stembundle ));
5339     gd->vbundle->stemlist = calloc( gd->stemcnt,sizeof( struct stemdata *));
5340     gd->vbundle->unit.x = 0; gd->vbundle->unit.y = 1;
5341     gd->vbundle->l_to_r.x = 1; gd->vbundle->l_to_r.y = 0;
5342 
5343     if ( gd->has_slant && !gd->only_hv ) {
5344 	SplineCharFindBounds( gd->sc,&bounds );
5345 
5346 	gd->ibundle = calloc( 1,sizeof( struct stembundle ));
5347 	gd->ibundle->stemlist = calloc( gd->stemcnt,sizeof( struct stemdata *));
5348 	gd->ibundle->unit.x = gd->slant_unit.x;
5349 	gd->ibundle->unit.y = gd->slant_unit.y;
5350 	gd->ibundle->l_to_r.x = -gd->ibundle->unit.y;
5351 	gd->ibundle->l_to_r.y = gd->ibundle->unit.x;
5352     }
5353 
5354     for ( i=0; i<gd->stemcnt; ++i ) {
5355 	stem = &gd->stems[i];
5356 	if ( stem->toobig > maxtoobig )
5357     continue;
5358 	hv = IsUnitHV( &stem->unit,true );
5359 
5360 	if ( hv == 1 ) {
5361 	    gd->hbundle->stemlist[(gd->hbundle->cnt)++] = stem;
5362 	    stem->bundle = gd->hbundle;
5363 	} else if ( hv == 2 ) {
5364 	    gd->vbundle->stemlist[(gd->vbundle->cnt)++] = stem;
5365 	    stem->bundle = gd->vbundle;
5366 	} else if ( gd->has_slant && !gd->only_hv &&
5367 	    RealNear( stem->unit.x,gd->slant_unit.x ) &&
5368 	    RealNear( stem->unit.y,gd->slant_unit.y )) {
5369 
5370 	    /* Move base point coordinates to the baseline to simplify */
5371 	    /* stem ordering and positioning relatively to each other  */
5372 	    stem->left.x -= (( stem->left.y - bounds.miny ) * stem->unit.x )/stem->unit.y;
5373 	    stem->right.x -= (( stem->right.y - bounds.miny ) * stem->unit.x )/stem->unit.y;
5374 	    dmove = ( stem->left.y - bounds.miny ) / stem->unit.y;
5375 	    stem->left.y = stem->right.y = bounds.miny;
5376 	    for ( j=0; j<stem->activecnt; j++ ) {
5377 		stem->active[j].start += dmove;
5378 		stem->active[j].end += dmove;
5379 	    }
5380 
5381 	    gd->ibundle->stemlist[(gd->ibundle->cnt)++] = stem;
5382 	    stem->bundle = gd->ibundle;
5383 	    stem->italic = true;
5384 	}
5385     }
5386     qsort( gd->hbundle->stemlist,gd->hbundle->cnt,sizeof( struct stemdata *),stem_cmp );
5387     qsort( gd->vbundle->stemlist,gd->vbundle->cnt,sizeof( struct stemdata *),stem_cmp );
5388     if ( gd->has_slant && !gd->only_hv )
5389 	qsort( gd->ibundle->stemlist,gd->ibundle->cnt,sizeof( struct stemdata *),stem_cmp );
5390 
5391     if ( !needs_deps )
5392 return;
5393     for ( i=0; i<gd->hbundle->cnt; i++ )
5394 	LookForMasterHVStem( gd->hbundle->stemlist[i],&gd->bd );
5395     for ( i=0; i<gd->hbundle->cnt; i++ )
5396 	ClearUnneededDeps( gd->hbundle->stemlist[i] );
5397     for ( i=0; i<gd->vbundle->cnt; i++ )
5398 	LookForMasterHVStem( gd->vbundle->stemlist[i],&gd->bd );
5399     for ( i=0; i<gd->vbundle->cnt; i++ )
5400 	ClearUnneededDeps( gd->vbundle->stemlist[i] );
5401 }
5402 
AddSerifOrBall(struct glyphdata * gd,struct stemdata * master,struct stemdata * slave,int lbase,int is_ball)5403 static void AddSerifOrBall( struct glyphdata *gd,
5404     struct stemdata *master,struct stemdata *slave,int lbase,int is_ball ) {
5405 
5406     struct dependent_serif *tserif;
5407     struct pointdata *spd, *bpd=NULL;
5408     double width, min, max;
5409     int i, j, refidx, scnt, next;
5410 
5411     if ( lbase ) {
5412 	width = fabs(
5413 		( slave->right.x - master->left.x ) * master->unit.y -
5414 		( slave->right.y - master->left.y ) * master->unit.x );
5415 	max = width + slave->rmin + 2*dist_error_hv;
5416 	min = width + slave->rmax - 2*dist_error_hv;
5417     } else {
5418 	width = fabs(
5419 		( master->right.x - slave->left.x ) * master->unit.y -
5420 		( master->right.y - slave->left.y ) * master->unit.x );
5421 	max = width - slave->lmax + 2*dist_error_hv;
5422 	min = width - slave->lmin - 2*dist_error_hv;
5423     }
5424 
5425     scnt = master->serif_cnt;
5426     for ( i=0; i<scnt; i++ ) {
5427 	tserif = &master->serifs[i];
5428 	if ( tserif->stem == slave && tserif->lbase == lbase )
5429     break;
5430 	else if ( tserif->width > min && tserif->width < max && tserif->lbase == lbase ) {
5431 	    for ( j=0; j<slave->chunk_cnt; j++ ) {
5432 		spd = ( lbase ) ? slave->chunks[j].r : slave->chunks[j].l;
5433 		next = ( lbase ) ? slave->chunks[j].rnext : slave->chunks[j].lnext;
5434 		if ( spd != NULL && IsStemAssignedToPoint( spd,tserif->stem,next ) == -1 )
5435 		    AddToStem( gd,tserif->stem,spd,NULL,next,false,false );
5436 	    }
5437     break;
5438 	}
5439     }
5440     if ( i<master->serif_cnt )
5441 return;
5442 
5443     refidx = ( lbase ) ? master->leftidx : master->rightidx;
5444     if ( refidx != -1 ) bpd = &gd->points[refidx];
5445     master->serifs = realloc(
5446 	master->serifs,( scnt+1 )*sizeof( struct dependent_serif ));
5447     master->serifs[scnt].stem = slave;
5448     master->serifs[scnt].width = width;
5449     master->serifs[scnt].lbase = lbase;
5450     master->serifs[scnt].is_ball = is_ball;
5451     master->serif_cnt++;
5452 
5453     /* Mark the dependent stem as related with a bundle, although it */
5454     /* is not listed in that bundle itself */
5455     slave->bundle = master->bundle;
5456 }
5457 
IsBall(struct glyphdata * gd,struct pointdata * pd,struct stemdata * master,int lbase)5458 static int IsBall( struct glyphdata *gd,
5459     struct pointdata *pd,struct stemdata *master,int lbase ) {
5460 
5461     double max, min, dot, coord;
5462     BasePoint *lbp, *rbp, *dir;
5463     Spline *test;
5464     struct pointdata *nbase, *pbase, *tpd;
5465     struct stem_chunk *chunk;
5466     int i, is_x, peak_passed;
5467 
5468     if ( pd == NULL || ( pd->x_extr != 1 && pd->y_extr != 1 ))
5469 return( false );
5470 
5471     is_x = ( IsUnitHV( &master->unit,true ) == 1 );
5472     lbp = ( lbase ) ? &master->left : &pd->base;
5473     rbp = ( lbase ) ? &pd->base : &master->right;
5474     min = ( is_x ) ? rbp->y : lbp->x;
5475     max = ( is_x ) ? lbp->y : rbp->x;
5476 
5477     peak_passed = false;
5478     nbase = pbase = NULL;
5479     test = pd->sp->next;
5480     dir = &pd->nextunit;
5481 
5482     if ( test != NULL ) do {
5483 	tpd = &gd->points[test->to->ptindex];
5484 	if ( IsStemAssignedToPoint( tpd,master,true ) != -1 ) {
5485 	    nbase = tpd;
5486     break;
5487 	}
5488 	coord = ( is_x ) ? tpd->base.y : tpd->base.x;
5489 	dot = tpd->nextunit.x * dir->x + tpd->nextunit.y * dir->y;
5490 	if ( dot == 0 && !peak_passed ) {
5491 	    dir = &tpd->nextunit;
5492 	    dot = 1.0;
5493 	    peak_passed = true;
5494 	}
5495 	test = test->to->next;
5496     } while ( test != NULL && test != pd->sp->next && dot > 0 &&
5497 	coord >= min && coord <= max );
5498 
5499     peak_passed = false;
5500     test = pd->sp->prev;
5501     dir = &pd->prevunit;
5502     if ( test != NULL ) do {
5503 	tpd = &gd->points[test->from->ptindex];
5504 	if ( IsStemAssignedToPoint( tpd,master,false ) != -1 ) {
5505 	    pbase = tpd;
5506     break;
5507 	}
5508 	coord = ( is_x ) ? tpd->base.y : tpd->base.x;
5509 	dot = tpd->prevunit.x * dir->x + tpd->prevunit.y * dir->y;
5510 	if ( dot == 0 && !peak_passed ) {
5511 	    dir = &tpd->prevunit;
5512 	    dot = 1.0;
5513 	    peak_passed = true;
5514 	}
5515 	test = test->from->prev;
5516     } while ( test != NULL && test != pd->sp->prev && dot > 0 &&
5517 	coord >= min && coord <= max );
5518 
5519     if ( nbase != NULL && pbase != NULL ) {
5520 	for ( i=0; i<master->chunk_cnt; i++ ) {
5521 	    chunk = &master->chunks[i];
5522 	    if (( chunk->l == nbase && chunk->r == pbase ) ||
5523 		( chunk->l == pbase && chunk->r == nbase ))
5524 return( true );
5525 	}
5526     }
5527 return( false );
5528 }
5529 
GetSerifData(struct glyphdata * gd,struct stemdata * stem)5530 static void GetSerifData( struct glyphdata *gd,struct stemdata *stem ) {
5531     int i, j, is_x, stem_cnt;
5532     int snext, enext, eidx, allow_s, allow_e, s_ball, e_ball;
5533     struct stem_chunk *chunk;
5534     struct stemdata *tstem, *smaster=NULL, *emaster=NULL;
5535     struct pointdata *spd, *epd;
5536     struct stembundle *bundle;
5537     double start, end, tstart, tend, smend, emstart;
5538 
5539     is_x = ( IsUnitHV( &stem->unit,true ) == 1 );
5540     bundle = ( is_x ) ? gd->hbundle : gd->vbundle;
5541     start = ( is_x ) ? stem->right.y : stem->left.x;
5542     end = ( is_x ) ? stem->left.y : stem->right.x;
5543 
5544     allow_s = allow_e = true;
5545     s_ball = e_ball = 0;
5546     for ( i=0; i<stem->chunk_cnt && ( allow_s == true || allow_e == true ); i++ ) {
5547 	chunk = &stem->chunks[i];
5548 	spd = ( is_x ) ? chunk->r : chunk->l;
5549 	snext = ( is_x ) ? chunk->rnext : chunk->lnext;
5550 	epd = ( is_x ) ? chunk->l : chunk->r;
5551 	enext = ( is_x ) ? chunk->lnext : chunk->rnext;
5552 
5553 	if ( spd != NULL && allow_e ) {
5554 	    stem_cnt = ( snext ) ? spd->nextcnt : spd->prevcnt;
5555 	    for ( j=0; j<stem_cnt; j++ ) {
5556 		tstem = ( snext ) ? spd->nextstems[j] : spd->prevstems[j];
5557 		if (RealNear( tstem->unit.x,stem->unit.x ) && RealNear( tstem->unit.y,stem->unit.y ) &&
5558 		    !tstem->toobig ) {
5559 		    chunk->is_ball = e_ball = IsBall( gd,epd,tstem,!is_x );
5560 		    if ( e_ball ) {
5561 			chunk->ball_m = tstem;
5562 			emaster = tstem;
5563 			emstart = ( is_x ) ? tstem->right.y : tstem->left.x;
5564 		    }
5565 		    allow_s = false;
5566 		}
5567 	    }
5568 
5569 	}
5570 	if ( epd != NULL && allow_s ) {
5571 	    stem_cnt = ( enext ) ? epd->nextcnt : epd->prevcnt;
5572 	    for ( j=0; j<stem_cnt; j++ ) {
5573 		tstem = ( enext ) ? epd->nextstems[j] : epd->prevstems[j];
5574 		if (tstem->unit.x == stem->unit.x && tstem->unit.y == stem->unit.y &&
5575 		    !tstem->toobig ) {
5576 		    chunk->is_ball = s_ball = IsBall( gd,spd,tstem,is_x );
5577 		    if ( s_ball ) {
5578 			chunk->ball_m = tstem;
5579 			smaster = tstem;
5580 			smend = ( is_x ) ? tstem->left.y : tstem->right.x;
5581 		    }
5582 		    allow_e = false;
5583 		}
5584 	    }
5585 
5586 	}
5587     }
5588 
5589     for ( i=0; i<bundle->cnt; i++ ) {
5590 	tstem = bundle->stemlist[i];
5591 	if (tstem->unit.x != stem->unit.x || tstem->unit.y != stem->unit.y ||
5592 	    tstem->toobig || tstem->width >= stem->width )
5593     continue;
5594 
5595 	tstart = ( is_x ) ? tstem->right.y : tstem->left.x;
5596 	tend = ( is_x ) ? tstem->left.y : tstem->right.x;
5597 
5598 	if ( tstart >= start && tend <= end ) {
5599 	    if ( allow_s && tstart > start ) {
5600 		for ( j=0; j<tstem->chunk_cnt && smaster != tstem; j++ ) {
5601 		    if ( is_x ) {
5602 			spd = tstem->chunks[j].l;
5603 			snext = tstem->chunks[j].lnext;
5604 			eidx = tstem->chunks[j].l_e_idx;
5605 		    } else {
5606 			spd = tstem->chunks[j].r;
5607 			snext = tstem->chunks[j].rnext;
5608 			eidx = tstem->chunks[j].r_e_idx;
5609 		    }
5610 		    if ( spd != NULL && ConnectsAcrossToStem( gd,spd,snext,stem,is_x,eidx ) &&
5611 			( smaster == NULL || smend - start > tend - start )) {
5612 			smaster = tstem;
5613 			smend = tend;
5614 		    }
5615 		}
5616 	    }
5617 	    if ( allow_e && tend < end ) {
5618 		for ( j=0; j<tstem->chunk_cnt && emaster != tstem; j++ ) {
5619 		    if ( is_x ) {
5620 			epd = tstem->chunks[j].r;
5621 			enext = tstem->chunks[j].rnext;
5622 			eidx = tstem->chunks[j].r_e_idx;
5623 		    } else {
5624 			epd = tstem->chunks[j].l;
5625 			enext = tstem->chunks[j].lnext;
5626 			eidx = tstem->chunks[j].l_e_idx;
5627 		    }
5628 		    if ( epd != NULL && ConnectsAcrossToStem( gd,epd,enext,stem,!is_x,eidx ) &&
5629 			( emaster == NULL || end - emstart > end - tstart )) {
5630 			emaster = tstem;
5631 			emstart = tstart;
5632 		    }
5633 		}
5634 	    }
5635 	}
5636     }
5637     if ( smaster != NULL )
5638 	AddSerifOrBall( gd,smaster,stem,is_x,s_ball );
5639     if ( emaster != NULL )
5640 	AddSerifOrBall( gd,emaster,stem,!is_x,e_ball );
5641 }
5642 
ActiveOverlap(struct stemdata * stem1,struct stemdata * stem2)5643 static double ActiveOverlap( struct stemdata *stem1,struct stemdata *stem2 ) {
5644     int is_x, i, j = 0;
5645     double base1, base2, s1, e1, s2, e2, s, e, len = 0;
5646 
5647     is_x = ( IsUnitHV( &stem1->unit,true ) == 2 );
5648     base1 = ( &stem1->left.x )[is_x];
5649     base2 = ( &stem2->left.x )[is_x];
5650 
5651     for ( i=0; i<stem1->activecnt; i++ ) {
5652 	s1 = base1 + stem1->active[i].start;
5653 	e1 = base1 + stem1->active[i].end;
5654 	for ( ; j<stem2->activecnt; j++ ) {
5655 	    s2 = base2 + stem2->active[j].start;
5656 	    e2 = base2 + stem2->active[j].end;
5657 	    if ( s2 > e1 )
5658 	break;
5659 
5660 	    if ( e2 < s1 )
5661 	continue;
5662 
5663 	    s = s2 < s1 ? s1 : s2;
5664 	    e = e2 > e1 ? e1 : e2;
5665 	    if ( e<s )
5666 	continue;		/* Shouldn't happen */
5667 	    len += e - s;
5668 	}
5669     }
5670 return( len );
5671 }
5672 
StemPairsSimilar(struct stemdata * s1,struct stemdata * s2,struct stemdata * ts1,struct stemdata * ts2)5673 static int StemPairsSimilar( struct stemdata *s1, struct stemdata *s2,
5674     struct stemdata *ts1, struct stemdata *ts2 ) {
5675 
5676     int normal, reversed, ret = 0;
5677     double olen1, olen2;
5678 
5679     /* Stem widths in the second pair should be nearly the same as */
5680     /* stem widths in the first pair */
5681     normal = (  ts1->width >= s1->width - dist_error_hv &&
5682 		ts1->width <= s1->width + dist_error_hv &&
5683 		ts2->width >= s2->width - dist_error_hv &&
5684 		ts2->width <= s2->width + dist_error_hv );
5685     reversed = (ts1->width >= s2->width - dist_error_hv &&
5686 		ts1->width <= s2->width + dist_error_hv &&
5687 		ts2->width >= s1->width - dist_error_hv &&
5688 		ts2->width <= s1->width + dist_error_hv );
5689 
5690     if ( !normal && !reversed )
5691 return( false );
5692 
5693     if ( normal ) {
5694 	olen1 = ActiveOverlap( s1, ts1 );
5695 	olen2 = ActiveOverlap( s2, ts2 );
5696 	ret =   olen1 > s1->clen/3 && olen1 > ts1->clen/3 &&
5697 		olen2 > s2->clen/3 && olen2 > ts2->clen/3;
5698     } else if ( reversed ) {
5699 	olen1 = ActiveOverlap( s1, ts2 );
5700 	olen2 = ActiveOverlap( s2, ts1 );
5701 	ret =   olen1 > s1->clen/3 && olen1 > ts2->clen/3 &&
5702 		olen2 > s2->clen/3 && olen2 > ts1->clen/3;
5703     }
5704 return( ret );
5705 }
5706 
FindCounterGroups(struct glyphdata * gd,int is_v)5707 static void FindCounterGroups( struct glyphdata *gd,int is_v ) {
5708     struct stembundle *bundle = is_v ? gd->vbundle : gd->hbundle;
5709     struct stemdata *curm, *prevm, *cur, *prev;
5710     int i, j;
5711     double mdist, dist;
5712 
5713     prevm = NULL;
5714     for ( i=0; i<bundle->cnt; i++ ) {
5715 	curm = prev = bundle->stemlist[i];
5716 	if ( curm->master != NULL )
5717     continue;
5718 	if ( prevm == NULL || curm->prev_c_m != NULL ) {
5719 	    prevm = curm;
5720     continue;
5721 	}
5722 	mdist = is_v ? curm->left.x - prevm->right.x : curm->right.y - prevm->left.y;
5723 	for ( j=i+1; j<bundle->cnt; j++ ) {
5724 	    cur = bundle->stemlist[j];
5725 	    if ( cur->master != NULL )
5726 	continue;
5727 	    if ( cur->prev_c_m != NULL ) {
5728 		prev = cur;
5729 	continue;
5730 	    }
5731 
5732 	    dist =  is_v ? cur->left.x - prev->right.x : cur->right.y - prev->left.y;
5733 	    if ( mdist > dist - dist_error_hv && mdist < dist + dist_error_hv &&
5734 		StemPairsSimilar( prevm,curm,prev,cur )) {
5735 		prev->next_c_m = prevm;
5736 		cur->prev_c_m = curm;
5737 	    }
5738 	    prev = cur;
5739 	}
5740 	prevm = curm;
5741     }
5742 }
5743 
5744 /* Normally we use the DetectDiagonalStems flag (set via the Preferences dialog) to determine */
5745 /* if diagonal stems should be generated. However, sometimes it makes sense to reduce the */
5746 /* processing time, deliberately turning the diagonal stem detection off: in particular we */
5747 /* don't need any diagonal stems if we only want to assign points to some preexisting HV */
5748 /* hints. For thisreason  the only_hv argument still can be passed to this function. */
GlyphDataInit(SplineChar * sc,int layer,double em_size,int only_hv)5749 struct glyphdata *GlyphDataInit( SplineChar *sc,int layer,double em_size,int only_hv ) {
5750     struct glyphdata *gd;
5751     struct pointdata *pd;
5752     int i;
5753     SplineSet *ss;
5754     SplinePoint *sp;
5755     Monotonic *m;
5756     int cnt;
5757     double iangle;
5758 
5759     if ( layer<0 || layer>=sc->layer_cnt )
5760         return( NULL );
5761 
5762     /* We only hint one layer at a time */
5763     /* We shan't try to hint references yet */
5764     if ( sc->layers[layer].splines==NULL )
5765 return( NULL );
5766 
5767     gd = calloc( 1,sizeof( struct glyphdata ));
5768     gd->only_hv = only_hv;
5769     gd->layer = layer;
5770 
5771     gd->sc = sc;
5772     gd->sf = sc->parent;
5773     gd->emsize = em_size;
5774     gd->order2 = ( sc->parent != NULL ) ? sc->parent->layers[layer].order2 : false;
5775     gd->fuzz = GetBlueFuzz( sc->parent );
5776 
5777     dist_error_hv = .0035*gd->emsize;
5778     dist_error_diag = .0065*gd->emsize;
5779     dist_error_curve = .022*gd->emsize;
5780 
5781     if ( sc->parent != NULL && sc->parent->italicangle ) {
5782 	iangle = ( 90 + sc->parent->italicangle );
5783 	gd->has_slant = true;
5784 	gd->slant_unit.x = cos( iangle * ( FF_PI/180 ));
5785 	gd->slant_unit.y = sin( iangle * ( FF_PI/180 ));
5786     } else {
5787 	gd->has_slant = false;
5788 	gd->slant_unit.x = 0; gd->slant_unit.y = 1;
5789     }
5790 
5791     /* SSToMContours can clean up the splinesets (remove 0 length splines) */
5792     /*  so it must be called BEFORE everything else (even though logically */
5793     /*  that doesn't make much sense). Otherwise we might have a pointer */
5794     /*  to something since freed */
5795     gd->ms = SSsToMContours(sc->layers[layer].splines,over_remove);	/* second argument is meaningless here */
5796 
5797     gd->realcnt = gd->pcnt = SCNumberPoints( sc, layer );
5798     for ( i=0, ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next, ++i );
5799     gd->ccnt = i;
5800     gd->contourends = malloc((i+1)*sizeof(int));
5801     for ( i=0, ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next, ++i ) {
5802 	SplinePoint *last;
5803 	if ( ss->first->prev!=NULL )
5804 	    last = ss->first->prev->from;
5805 	else
5806 	    last = ss->last;
5807 	if ( last->ttfindex==0xffff )
5808 	    gd->contourends[i] = last->nextcpindex;
5809 	else
5810 	    gd->contourends[i] = last->ttfindex;
5811     }
5812     gd->contourends[i] = -1;
5813 
5814     /* Create temporary point numbers for the implied points. We need this */
5815     /*  for metafont if nothing else */
5816     for ( ss= sc->layers[layer].splines; ss!=NULL; ss = ss->next ) {
5817 	for ( sp = ss->first; ; ) {
5818 	    if ( sp->ttfindex < gd->realcnt )
5819 		sp->ptindex = sp->ttfindex;
5820 	    else if ( sp->ttfindex == 0xffff )
5821 		sp->ptindex = gd->pcnt++;
5822 	    if ( sp->next==NULL )
5823 	break;
5824 	    sp = sp->next->to;
5825 	    if ( sp==ss->first )
5826 	break;
5827 	}
5828     }
5829     gd->norefpcnt = gd->pcnt;
5830     /* And for 0xfffe points such as those used in glyphs with order2 glyphs */
5831     /*  with references. */
5832     for ( ss = sc->layers[layer].splines; ss!=NULL; ss = ss->next ) {
5833 	for ( sp = ss->first; ; ) {
5834 	    if ( sp->ttfindex == 0xfffe )
5835 		sp->ptindex = gd->pcnt++;
5836 	    if ( sp->next==NULL )
5837 	break;
5838 	    sp = sp->next->to;
5839 	    if ( sp==ss->first )
5840 	break;
5841 	}
5842     }
5843     gd->pspace = malloc( gd->pcnt*sizeof( struct pointdata *));
5844 
5845     /*gd->ms = SSsToMContours(sc->layers[layer].splines,over_remove);*/	/* second argument is meaningless here */
5846     for ( m=gd->ms, cnt=0; m!=NULL; m=m->linked, ++cnt );
5847     gd->space = malloc((cnt+2)*sizeof(Monotonic*));
5848     gd->mcnt = cnt;
5849 
5850     gd->points = calloc(gd->pcnt,sizeof(struct pointdata));
5851     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) if ( ss->first->prev!=NULL ) {
5852 	for ( sp=ss->first; ; ) {
5853 	    PointInit( gd,sp,ss );
5854 	    if ( sp->next==NULL )
5855 	break;
5856 	    sp = sp->next->to;
5857 	    if ( sp==ss->first )
5858 	break;
5859 	}
5860     }
5861 
5862     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
5863 	pd = &gd->points[i];
5864 	if ( !pd->nextzero )
5865 	    pd->next_e_cnt = FindMatchingEdge(gd,pd,true,pd->nextedges);
5866 	if ( !pd->prevzero )
5867 	    pd->prev_e_cnt = FindMatchingEdge(gd,pd,false,pd->prevedges);
5868 	if (( pd->symetrical_h || pd->symetrical_v ) && ( pd->x_corner || pd->y_corner))
5869 	    FindMatchingEdge(gd,pd,2,&pd->bothedge);
5870     }
5871 
5872 return( gd );
5873 }
5874 
GlyphDataBuild(SplineChar * sc,int layer,BlueData * bd,int use_existing)5875 struct glyphdata *GlyphDataBuild( SplineChar *sc,int layer, BlueData *bd,int use_existing ) {
5876     struct glyphdata *gd;
5877     struct pointdata *pd;
5878     struct stemdata *stem;
5879     BasePoint dir;
5880     struct stem_chunk *chunk;
5881     int i, j, only_hv, startcnt, stemcnt, ecnt, hv, has_h, has_v;
5882     double em_size;
5883     DBounds bounds;
5884 
5885     only_hv = ( !detect_diagonal_stems && ( !use_existing || sc->dstem == NULL ));
5886     em_size = ( sc->parent != NULL ) ? sc->parent->ascent + sc->parent->descent : 1000;
5887 
5888     gd = GlyphDataInit( sc,layer,em_size,only_hv );
5889     if ( gd == NULL )
5890 return( gd );
5891     /* Get the alignment zones */
5892     if ( bd == NULL )
5893 	QuickBlues( gd->sf,gd->layer,&gd->bd );
5894     else
5895 	memcpy( &gd->bd,bd,sizeof( BlueData ));
5896 
5897     /* There will never be more lines than there are points (counting next/prev as separate) */
5898     gd->lines = malloc( 2*gd->pcnt*sizeof( struct linedata ));
5899     gd->linecnt = 0;
5900     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
5901 	pd = &gd->points[i];
5902 	if (( !gd->only_hv || pd->next_hor || pd->next_ver ) && pd->nextline==NULL ) {
5903 	    pd->nextline = BuildLine(gd,pd,true);
5904 	    if ( pd->colinear )
5905 		pd->prevline = pd->nextline;
5906 	}
5907 	if (( !gd->only_hv || pd->prev_hor || pd->prev_ver ) && pd->prevline==NULL ) {
5908 	    pd->prevline = BuildLine(gd,pd,false);
5909 	    if ( pd->colinear && pd->nextline == NULL )
5910 		pd->nextline = pd->prevline;
5911 	}
5912     }
5913 
5914     /* There will never be more stems than there are points (counting next/prev as separate) */
5915     gd->stems = calloc( 2*gd->pcnt,sizeof( struct stemdata ));
5916     gd->stemcnt = 0;			/* None used so far */
5917 
5918     if ( use_existing ) {
5919 	SplineCharFindBounds( gd->sc,&bounds );
5920 	if ( sc->vstem != NULL )
5921 	    _StemInfoToStemData( gd,sc->vstem,&bounds,true,&startcnt );
5922 	if ( sc->hstem != NULL )
5923 	    _StemInfoToStemData( gd,sc->hstem,&bounds,false,&startcnt );
5924 	if ( sc->dstem != NULL )
5925 	    _DStemInfoToStemData( gd,sc->dstem,&startcnt );
5926     }
5927 
5928     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp!=NULL ) {
5929 	pd = &gd->points[i];
5930 	if ( pd->prev_e_cnt > 0 ) {
5931 	    ecnt = BuildStem( gd,pd,false,false,use_existing,0 );
5932 	    if ( ecnt == 0 && pd->prev_e_cnt > 1 )
5933 		BuildStem( gd,pd,false,false,false,1 );
5934 	}
5935 	if ( pd->next_e_cnt > 0 ) {
5936 	    ecnt = BuildStem( gd,pd,true,false,use_existing,0 );
5937 	    if ( ecnt == 0 && pd->next_e_cnt > 1 )
5938 		BuildStem( gd,pd,true,false,false,1 );
5939 	}
5940 	if ( pd->bothedge!=NULL ) {
5941 	    DiagonalCornerStem( gd,pd,false );
5942 	}
5943 
5944 	/* Snap corner extrema to preexisting hints if they have not */
5945 	/* already been. This is currently done only when preparing  */
5946 	/* glyph data for the autoinstructor */
5947 	if ( use_existing && ( pd->x_corner || pd->y_corner )) {
5948 	    has_h = has_v = false;
5949 	    for ( j=0; j<pd->prevcnt && (( pd->x_corner && !has_v ) || ( pd->y_corner && !has_h )); j++ ) {
5950 		hv = IsUnitHV( &pd->prevstems[j]->unit,true );
5951 		if ( hv == 1 ) has_h = true;
5952 		else if ( hv == 2 ) has_v = true;
5953 	    }
5954 	    for ( j=0; j<pd->nextcnt && (( pd->x_corner && !has_v ) || ( pd->y_corner && !has_h )); j++ ) {
5955 		hv = IsUnitHV( &pd->nextstems[j]->unit,true );
5956 		if ( hv == 1 ) has_h = true;
5957 		else if ( hv == 2 ) has_v = true;
5958 	    }
5959 	    if ( pd->x_corner && !has_v ) {
5960 		dir.x = 0; dir.y = 1;
5961 		HalfStemNoOpposite( gd,pd,NULL,&dir,2 );
5962 	    } else if ( pd->y_corner && !has_h ) {
5963 		dir.x = 1; dir.y = 0;
5964 		HalfStemNoOpposite( gd,pd,NULL,&dir,2 );
5965 	    }
5966 	}
5967     }
5968     AssignLinePointsToStems( gd );
5969 
5970     /* Normalize stems before calculating active zones (as otherwise */
5971     /* we don't know exact positions of stem edges */
5972     for ( i=0; i<gd->stemcnt; ++i )
5973 	NormalizeStem( gd,&gd->stems[i] );
5974     GDNormalizeStubs( gd );
5975 
5976     /* Figure out active zones at the first order (as they are needed to */
5977     /* determine which stems are undesired and they don't depend from */
5978     /* the "potential" state of left/right points in chunks */
5979     gd->lspace = malloc(gd->pcnt*sizeof(struct segment));
5980     gd->rspace = malloc(gd->pcnt*sizeof(struct segment));
5981     gd->bothspace = malloc(3*gd->pcnt*sizeof(struct segment));
5982     gd->activespace = malloc(3*gd->pcnt*sizeof(struct segment));
5983 #if GLYPH_DATA_DEBUG
5984     fprintf( stderr,"Going to calculate stem active zones for %s\n",gd->sc->name );
5985 #endif
5986     for ( i=0; i<gd->stemcnt; ++i )
5987 	FigureStemActive( gd,&gd->stems[i] );
5988 
5989     /* Check this before resolving stem conflicts, as otherwise we can */
5990     /* occasionally prefer a stem which should be excluded from the list */
5991     /* for some other reasons */
5992     GDFindUnlikelyStems( gd );
5993 
5994     /* we were cautious about assigning points to stems, go back now and see */
5995     /*  if there are any low-quality matches which remain unassigned, and if */
5996     /*  so then assign them to the stem they almost fit on. */
5997     for ( i=0; i<gd->stemcnt; ++i ) {
5998 	stem = &gd->stems[i];
5999 	for ( j=0; j<stem->chunk_cnt; ++j ) {
6000 	    chunk = &stem->chunks[j];
6001 	    if ( chunk->l!=NULL && chunk->lpotential ) {
6002 		stemcnt = ( chunk->lnext ) ? chunk->l->nextcnt : chunk->l->prevcnt;
6003 		if ( stemcnt == 1 ) chunk->lpotential = false;
6004 	    }
6005 	    if ( chunk->r!=NULL && chunk->rpotential ) {
6006 		stemcnt = ( chunk->rnext ) ? chunk->r->nextcnt : chunk->r->prevcnt;
6007 		if ( stemcnt == 1 ) chunk->rpotential = false;
6008 	    }
6009 	}
6010     }
6011     /* If there are multiple stems, find the one which is closest to this point */
6012     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp != NULL ) {
6013 	pd = &gd->points[i];
6014 	if ( pd->prevcnt > 1 ) CheckPotential( gd,pd,false );
6015 	if ( pd->nextcnt > 1 ) CheckPotential( gd,pd,true );
6016     }
6017 
6018     if ( hint_bounding_boxes )
6019 	CheckForBoundingBoxHints( gd );
6020     CheckForGhostHints( gd );
6021     if ( use_existing )
6022         MarkDStemCorners( gd );
6023 
6024     GDBundleStems( gd,0,use_existing );
6025     if ( use_existing ) {
6026 	for ( i=0; i<gd->stemcnt; ++i ) {
6027 	    stem = &gd->stems[i];
6028 	    if ( stem->toobig == 1 && IsUnitHV( &stem->unit,true ))
6029 		GetSerifData( gd,stem );
6030 	}
6031 	FindCounterGroups( gd,true );
6032     }
6033 
6034 #if GLYPH_DATA_DEBUG
6035     DumpGlyphData( gd );
6036 #endif
6037     free(gd->lspace);		gd->lspace = NULL;
6038     free(gd->rspace);		gd->rspace = NULL;
6039     free(gd->bothspace);	gd->bothspace = NULL;
6040     free(gd->activespace);	gd->activespace = NULL;
6041 
6042 return( gd );
6043 }
6044 
GlyphDataFree(struct glyphdata * gd)6045 void GlyphDataFree(struct glyphdata *gd) {
6046     int i;
6047     if ( gd == NULL )
6048 return;
6049 
6050     FreeMonotonics( gd->ms );	gd->ms = NULL;
6051     free( gd->space );		gd->space = NULL;
6052     free( gd->sspace );		gd->sspace = NULL;
6053     free( gd->stspace );	gd->stspace = NULL;
6054     free( gd->pspace );		gd->pspace = NULL;
6055 
6056     /* Clean up temporary point numbers */
6057     for ( i=0; i<gd->pcnt; ++i ) if ( gd->points[i].sp != NULL )
6058 	gd->points[i].sp->ptindex = 0;
6059 
6060     if ( gd->hbundle != NULL ) {
6061 	free( gd->hbundle->stemlist );
6062 	free( gd->hbundle );
6063     }
6064     if ( gd->vbundle != NULL ) {
6065 	free( gd->vbundle->stemlist );
6066 	free( gd->vbundle );
6067     }
6068     if ( gd->ibundle != NULL ) {
6069 	free( gd->ibundle->stemlist );
6070 	free( gd->ibundle );
6071     }
6072 
6073     for ( i=0; i<gd->linecnt; ++i )
6074 	free( gd->lines[i].points );
6075     for ( i=0; i<gd->stemcnt; ++i ) {
6076 	free( gd->stems[i].chunks );
6077 	free( gd->stems[i].dependent );
6078 	free( gd->stems[i].serifs );
6079 	free( gd->stems[i].active );
6080     }
6081     for ( i=0; i<gd->pcnt; ++i ) {
6082 	free( gd->points[i].nextstems );
6083 	free( gd->points[i].next_is_l );
6084 	free( gd->points[i].prevstems );
6085 	free( gd->points[i].prev_is_l );
6086     }
6087     free( gd->lines );
6088     free( gd->stems );
6089     free( gd->contourends );
6090     free( gd->points );
6091     free( gd );
6092 }
6093