1 /* Copyright (C) 2007-2012 by George Williams */
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 "autohint.h"
31 #include "cvundoes.h"
32 #include "dumppfa.h"
33 #include "fontforgevw.h"
34 #include "fvcomposite.h"
35 #include "fvfonts.h"
36 #include "lookups.h"
37 #include "namelist.h"
38 #include "scstyles.h"
39 #include "splineorder2.h"
40 #include "splineoverlap.h"
41 #include "splinestroke.h"
42 #include "splineutil.h"
43 #include "splineutil2.h"
44 #include "stemdb.h"
45 #include "tottfgpos.h"
46 #include "ttf.h"
47 #include "ustring.h"
48 #include "utype.h"
49 
50 #include <math.h>
51 
52 static unichar_t lc_stem_str[] = { 'l', 'l', 'l', 'm', 'f', 't', 0x438, 0x43D,
53 	0x43f, 0x448, 0x3b9, 0 };
54 static unichar_t uc_stem_str[] = { 'I', 'L', 'T', 'H', 0x3a0, 0x397, 0x399,
55 	0x406, 0x418, 0x41d, 0x41f, 0x422, 0x428, 0 };
56 static unichar_t lc_botserif_str[] = { 'i', 'k', 'l', 'm', 'f', 0x433, 0x43a,
57 	0x43f, 0x442, 0x3c0, 0x3ba, 0 };
58 static unichar_t lc_topserif_str[] = { 'k', 'l', 'm', 0x444, 0x3b9, 0 };
59 static unichar_t descender_str[] = { 'p', 'q', 0x3b7, 0x3c1, 0x440, 0x444, 0 };
60 
SSCPValidate(SplineSet * ss)61 static void SSCPValidate(SplineSet *ss) {
62     SplinePoint *sp, *nsp;
63 
64     while ( ss!=NULL ) {
65 	for ( sp=ss->first; ; ) {
66 	    if ( sp->next==NULL )
67 	break;
68 	    nsp = sp->next->to;
69 	    if ( !sp->nonextcp && (sp->nextcp.x!=nsp->prevcp.x || sp->nextcp.y!=nsp->prevcp.y))
70 		IError( "Invalid 2nd order" );
71 	    sp = nsp;
72 	    if ( sp==ss->first )
73 	break;
74 	}
75 	ss = ss->next;
76     }
77 }
78 
SplineSetRefigure(SplineSet * ss)79 static void SplineSetRefigure(SplineSet *ss) {
80     Spline *s, *first;
81 
82     while ( ss!=NULL ) {
83 	first = NULL;
84 	for ( s=ss->first->next; s!=NULL && s!=first; s=s->to->next ) {
85 	    if ( first == NULL ) first = s;
86 	    SplineRefigure(s);
87 	}
88 	ss = ss->next;
89     }
90 }
91 
BoldSSStroke(SplineSet * ss,StrokeInfo * si,int order2,int ro)92 static SplineSet *BoldSSStroke(SplineSet *ss,StrokeInfo *si,int order2,int ro) {
93     SplineSet *temp;
94     Spline *s1, *s2;
95 
96     /* We don't want to use the remove overlap built into SplineSetStroke because */
97     /*  only looks at one contour at a time, but we need to look at all together */
98     /*  else we might get some unreal internal hints that will screw up */
99     /*  counter correction */
100     temp = SplineSetStroke(ss,si,order2);
101     if ( ro && temp!=NULL && SplineSetIntersect(temp,&s1,&s2))
102 	temp = SplineSetRemoveOverlap(NULL,temp,over_remove);
103 return( temp );
104 }
105 
106 /* ************************************************************************** */
107 /* ***************** Generic resize /stem control routines. ***************** */
108 /* ************* Used also for building small caps and friends. ************* */
109 /* ************************************************************************** */
110 
IsAnglePoint(SplinePoint * sp)111 static int IsAnglePoint(SplinePoint *sp) {
112     SplinePoint *psp, *nsp;
113     double PrevTangent, NextTangent;
114 
115     if (sp->next == NULL || sp->prev == NULL ||
116 	sp->pointtype != pt_corner || sp->ttfindex == 0xffff)
117 return( false );
118 
119     psp = sp->prev->from;
120     nsp = sp->next->to;
121     PrevTangent = atan2(sp->me.y - psp->me.y, sp->me.x - psp->me.x);
122     NextTangent = atan2(nsp->me.y - sp->me.y, nsp->me.x - sp->me.x);
123 
124 return fabs(PrevTangent - NextTangent) > 0.261;
125 }
126 
IsExtremum(SplinePoint * sp,int xdir)127 static int IsExtremum(SplinePoint *sp,int xdir) {
128     SplinePoint *psp, *nsp;
129     real val = (&sp->me.x)[xdir];
130 
131     if (sp->next == NULL || sp->prev == NULL )
132 return( false );
133 
134     psp = sp->prev->from;
135     nsp = sp->next->to;
136 return ((val < (&psp->me.x)[xdir] && val < (&nsp->me.x)[xdir]) ||
137 	(val > (&psp->me.x)[xdir] && val > (&nsp->me.x)[xdir]));
138 }
139 
InterpolateVal(double a,double b,double a1,double b1,double val)140 static double InterpolateVal( double a,double b,double a1, double b1, double val ) {
141 return( a1 + ( val - a ) * ( b1 - a1 )/( b - a ));
142 }
143 
IsPointFixed(PointData * pd)144 static int IsPointFixed( PointData *pd ) {
145 return (( pd->touched & tf_x && pd->touched & tf_y ) ||
146 	( pd->touched & tf_x && pd->touched & tf_d ) ||
147 	( pd->touched & tf_y && pd->touched & tf_d ));
148 }
149 
active_cmp(const void * _s1,const void * _s2)150 static int active_cmp(const void *_s1, const void *_s2) {
151     const struct segment *s1 = _s1, *s2 = _s2;
152     if ( s1->start<s2->start )
153 return( -1 );
154     else if ( s1->start>s2->start )
155 return( 1 );
156 
157 return( 0 );
158 }
159 
fixed_cmp(const void * _s1,const void * _s2)160 static int fixed_cmp(const void *_s1, const void *_s2) {
161     const struct position_maps *s1 = _s1, *s2 = _s2;
162     if ( s1->current<s2->current )
163 return( -1 );
164     else if ( s1->current>s2->current )
165 return( 1 );
166 
167 return( 0 );
168 }
169 
ds_cmp(const void * _s1,const void * _s2)170 static int ds_cmp( const void *_s1, const void *_s2 ) {
171     StemData * const *s1 = _s1, * const *s2 = _s2;
172 
173     BasePoint *bp1, *bp2;
174     bp1 = (*s1)->unit.y > 0 ? &(*s1)->keypts[0]->base : &(*s1)->keypts[2]->base;
175     bp2 = (*s2)->unit.y > 0 ? &(*s2)->keypts[0]->base : &(*s2)->keypts[2]->base;
176     if ( bp1->x < bp2->x || ( bp1->x == bp2->x && bp1->y < bp2->y ))
177 return( -1 );
178     else if ( bp2->x < bp1->x || ( bp2->x == bp1->x && bp2->y < bp1->y ))
179 return( 1 );
180 
181 return( 0 );
182 }
183 
184 #define czone_top 2
185 #define czone_bot 1
GetStemCounterZone(StemData * stem,DBounds * orig_b)186 static uint8 GetStemCounterZone( StemData *stem, DBounds *orig_b ) {
187     uint8 ret = 0, by_x;
188     int i;
189     double min, max, middle, fudge, s, e;
190 
191     if ( stem == NULL )
192 return( czone_top | czone_bot );
193     by_x = ( stem->unit.x > stem->unit.y );
194     min = by_x ? orig_b->minx : orig_b->miny;
195     max = by_x ? orig_b->maxx : orig_b->maxy;
196     middle = ( max - min )/2.;
197     fudge = ( max - min )/16.;
198 
199     for ( i=0; i<stem->activecnt && ret < 3; i++ ) {
200 	s = (&stem->left.x)[!by_x] +
201 	    stem->active[i].start * (&stem->unit.x)[!by_x];
202 	e = (&stem->left.x)[!by_x] +
203 	    stem->active[i].end * (&stem->unit.x)[!by_x];
204 	if ( s < middle - fudge || e < middle - fudge )
205 	    ret |= czone_bot;
206 	if ( e > middle + fudge || s > middle + fudge )
207 	    ret |= czone_top;
208     }
209 return( ret );
210 }
211 
GetCounterBlackSpace(GlyphData * gd,StemData ** dstems,int dcnt,DBounds * orig_b,double cstart,double cend,double pos,uint8 czone,struct genericchange * gc,int x_dir)212 static double GetCounterBlackSpace( GlyphData *gd, StemData **dstems, int dcnt,
213     DBounds *orig_b, double cstart, double cend, double pos, uint8 czone, struct genericchange *gc, int x_dir ) {
214 
215     double ldist, rdist, lrdist, is, ie, temp, black=0;
216     double scale, w;
217     struct segment *inters;
218     StemBundle *bundle;
219     StemData *stem;
220     int i, j, icnt=0;
221 
222     bundle = x_dir ? gd->vbundle : gd->hbundle;
223     inters = calloc( dcnt + bundle->cnt,sizeof( struct segment ));
224 
225     for ( i=0; i<dcnt; i++ ) {
226 	stem = dstems[i];
227 	/* If a stem is more horizontal than vertical, then it should not
228 	 * cause vertical counters to be increased, and vice versa.
229 	 * However we have to check both directions if stem slope angle
230 	 * is close to 45 degrees, as otherwise undesired random effects
231 	 * can occur (e. g. in a multiply sign). So we have selected 0.75
232 	 * (sin(48 deg. 36'), cos(41 deg. 24 min)) as a boundary value
233 	 */
234 	if (( x_dir && fabs( stem->unit.x ) > .75 ) ||
235 	    ( !x_dir && fabs( stem->unit.y ) > .75 ))
236     continue;
237 	ldist = ( pos - (&stem->left.x)[x_dir] )/(&stem->unit.x)[x_dir];
238 	rdist = ( pos - (&stem->right.x)[x_dir] )/(&stem->unit.x)[x_dir];
239 	lrdist =( stem->right.x - stem->left.x ) * stem->unit.x +
240 		( stem->right.y - stem->left.y ) * stem->unit.y;
241 
242 	for ( j=0; j<stem->activecnt; j++ ) {
243 	    if (ldist >= stem->active[j].start && ldist <= stem->active[j].end &&
244 		rdist + lrdist >= stem->active[j].start && rdist + lrdist <= stem->active[j].end ) {
245 
246 		is = (&stem->left.x)[!x_dir] + ldist * (&stem->unit.x)[!x_dir];
247 		ie = (&stem->right.x)[!x_dir] + rdist * (&stem->unit.x)[!x_dir];
248 		if ( is > ie ) {
249 		    temp = is; is = ie; ie = temp;
250 		}
251 		if ( is >= cstart && is < cend ) {
252 		    inters[icnt  ].start = is;
253 		    inters[icnt++].end = ( ie < cend ) ? ie : cend;
254 		} else if ( ie > cstart && ie <=cend ) {
255 		    inters[icnt  ].start = ( is > cstart ) ? is : cstart;
256 		    inters[icnt++].end = ie;
257 		}
258 	    }
259 	}
260     }
261     for ( i=0; i<bundle->cnt; i++ ) {
262 	stem = bundle->stemlist[i];
263 	if ( stem->bbox )
264     continue;
265 	is = x_dir ? stem->left.x : stem->right.y;
266 	ie = x_dir ? stem->right.x : stem->left.y;
267 
268 	if ( is >= cstart && is < cend && GetStemCounterZone( stem,orig_b ) & czone ) {
269 	    inters[icnt  ].start = is;
270 	    inters[icnt++].end = ( ie < cend ) ? ie : cend;
271 	} else if ( ie > cstart && ie <=cend && GetStemCounterZone( stem,orig_b ) & czone  ) {
272 	    inters[icnt  ].start = ( is > cstart ) ? is : cstart;
273 	    inters[icnt++].end = ie;
274 	}
275     }
276     qsort( inters,icnt,sizeof(struct segment),active_cmp );
277 
278     for ( i=j=0; i<icnt; i++ ) {
279 	if ( i == 0 ) {
280 	    w = ( inters[i].end - inters[i].start );
281 	} else {
282 	    while ( i<icnt && inters[i].end <= inters[j].end ) i++;
283 	    if ( i == icnt )
284     break;
285 	    if ( inters[i].start < inters[i-1].end )
286 		w = ( inters[i].end - inters[i-1].end );
287 	    else
288 		w = ( inters[i].end - inters[i].start );
289 	}
290 	if (gc->stem_threshold > 0) {
291 	    scale = w > gc->stem_threshold ? gc->stem_width_scale : gc->stem_height_scale;
292 	} else {
293 	    scale = x_dir ? gc->stem_width_scale : gc->stem_height_scale;
294 	}
295 	black += w*scale;
296 	j = i;
297     }
298     free( inters );
299 return( black );
300 }
301 
302 /* As with expanding/condensing, we are going to assume that LCG glyphs have
303  * at most two counter zones, one near the bottom (baseline), one near the top.
304  * So check if the given counter is intersected by some DStems at two positions:
305  * 25% glyph height and 75% glyph height. If so, then only the "white" part
306  * of the counter can be scaled by the normal counter ratio, while for the space
307  * covered by DStems the stem ratio should be used instead.
308  * However if at least one of the stems which form the counter doesn't extend
309  * to the top/bottom part of the glyph (Latin "Y" is the most obvious example),
310  * then the actual counter width in that part is larger than just the space
311  * between two stems (or a stem and a glyph boundary), so that expanding just
312  * that space would make the counter disproportionally wide. We are attempting
313  * to compensate this by subtracting a half base stem width from the "white"
314  * part of the counter
315  */
ScaleCounter(GlyphData * gd,StemData ** dstems,int dcnt,DBounds * orig_b,StemData * pstem,StemData * nstem,struct genericchange * gc,int x_dir)316 static double ScaleCounter( GlyphData *gd, StemData **dstems, int dcnt,
317     DBounds *orig_b, StemData *pstem, StemData *nstem, struct genericchange *gc, int x_dir ) {
318 
319     double min, max, onequarter, threequarters, cstart, cend, cntr_scale;
320     double black25, black75, white25, white75, gen25, gen75, ret;
321     uint8 pczone, nczone;
322 
323     min = x_dir ? orig_b->minx : orig_b->miny;
324     max = x_dir ? orig_b->maxx : orig_b->maxy;
325     cntr_scale = x_dir ? gc->hcounter_scale : gc->vcounter_scale;
326     cstart = ( pstem != NULL ) ? x_dir ? pstem->right.x : pstem->left.y : min;
327     cend = ( nstem != NULL ) ? x_dir ? nstem->left.x : nstem->right.y : max;
328     if ( cend == cstart )
329 return( 0 );
330 
331     pczone = GetStemCounterZone( pstem,orig_b );
332     nczone = GetStemCounterZone( nstem,orig_b );
333 
334     min = x_dir ? orig_b->miny : orig_b->minx;
335     max = x_dir ? orig_b->maxy : orig_b->maxx;
336     onequarter = min + ( max - min )*.25;
337     threequarters = min + ( max - min )*.75;
338     black25 = GetCounterBlackSpace( gd,dstems,dcnt,orig_b,cstart,cend,onequarter,czone_bot,gc,x_dir );
339     black75 = GetCounterBlackSpace( gd,dstems,dcnt,orig_b,cstart,cend,threequarters,czone_top,gc,x_dir );
340     white25 = cend - cstart - black25;
341     white75 = cend - cstart - black75;
342 
343     if ( !(pczone & czone_top) && white75 > white25 + pstem->width/2 )
344 	white75 -= pstem->width/2;
345     if ( !(nczone & czone_top) && white75 > white25 + nstem->width/2 )
346 	white75 -= nstem->width/2;
347     if ( !(pczone & czone_bot) && white25 > white75 + pstem->width/2 )
348 	white25 -= pstem->width/2;
349     if ( !(nczone & czone_bot) && white25 > white75 + nstem->width/2 )
350 	white25 -= nstem->width/2;
351     gen25 = white25 * cntr_scale + black25;
352     gen75 = white75 * cntr_scale + black75;
353     ret = ( gen25 > gen75 ) ? gen25 : gen75;
354 
355 return( ret );
356 }
357 
StemPosDependent(StemData * stem,struct genericchange * genchange,int x_dir)358 static void StemPosDependent( StemData *stem,struct genericchange *genchange,int x_dir ) {
359 
360     int i, lbase, expanded;
361     StemData *slave;
362     double dist, l, r, l1, r1, sl, sr;
363     double stem_scale, stem_add, stroke_add, serif_scale, width_new;
364 
365     expanded = (genchange->stem_width_add != 0 && genchange->stem_height_add !=0 &&
366 		genchange->stem_height_add/genchange->stem_width_add > 0 );
367     l = (&stem->left.x)[!x_dir]; r = (&stem->right.x)[!x_dir];
368     l1 = (&stem->newleft.x)[!x_dir]; r1 = (&stem->newright.x)[!x_dir];
369     if ( x_dir ) {
370 	stem_scale = genchange->stem_width_scale;
371 	stem_add = genchange->stem_width_add;
372 	serif_scale = genchange->serif_width_scale;
373     } else {
374 	stem_scale = genchange->stem_height_scale;
375 	stem_add = genchange->stem_height_add;
376 	serif_scale = genchange->serif_height_scale;
377     }
378     stroke_add = expanded ? stem_add : 0;
379 
380     for (i=0; i<stem->dep_cnt; i++) {
381 	slave = stem->dependent[i].stem;
382 	lbase = stem->dependent[i].lbase;
383 	if ( genchange->stem_threshold > 0 ) {
384 	    stem_scale = slave->width > genchange->stem_threshold ?
385 		genchange->stem_width_scale : genchange->stem_height_scale;
386 	    stem_add = genchange->stem_height_add;
387 	}
388 	stroke_add = expanded ? stem_add : 0;
389 
390 	if ( !slave->ldone && !slave->rdone ) {
391 	    width_new = ( slave->width - stroke_add )*stem_scale + stem_add;
392 	    sl = (&slave->left.x)[!x_dir]; sr = (&slave->right.x)[!x_dir];
393 	    if ( !x_dir ) width_new = -width_new;
394 	    if (stem->dependent[i].dep_type == 'a' || stem->dependent[i].dep_type == 'm') {
395 		dist = lbase ? sl - l : sr - r;
396 		dist *= stem_scale;
397 		if (lbase) (&slave->newleft.x)[!x_dir] = l1 + floor( dist + .5 );
398 		else (&slave->newright.x)[!x_dir] = r1 + floor( dist + .5 );
399 	    } else if (stem->dependent[i].dep_type == 'i') {
400 		if (lbase)
401 		    (&slave->newleft.x)[!x_dir] = floor( InterpolateVal( l,r,l1,r1,sl ) + .5 );
402 		else
403 		    (&slave->newright.x)[!x_dir] = floor( InterpolateVal( l,r,l1,r1,sr ) + .5 );
404 	    }
405 	    if (lbase)
406 		(&slave->newright.x)[!x_dir] = (&slave->newleft.x)[!x_dir] + floor( width_new + .5 );
407 	    else
408 		(&slave->newleft.x)[!x_dir] = (&slave->newright.x)[!x_dir] - floor( width_new + .5 );
409 	}
410 	if ( slave->dep_cnt > 0 )
411 	    StemPosDependent( slave,genchange,x_dir );
412     }
413 
414     if ( genchange->serif_control ) {
415 	for (i=0; i<stem->serif_cnt; i++) {
416 	    slave = stem->serifs[i].stem;
417 	    lbase = stem->serifs[i].lbase;
418 	    /* In the autoinstructor we usually link an edge of the serif stem to the opposite
419 	     * edge of the main stem. So, if 'lbase' is true, this actually means that we are
420 	     * interested in the right edge of the serif stem. However, here, despite
421 	     * the variable name, we position the left edge of the serif relatively to the
422 	     * left edge of the master stem and vice versa
423 	     */
424 	    dist = lbase ? (&slave->right.x)[!x_dir] - r : l - (&slave->left.x)[!x_dir];
425 	    dist *= serif_scale;
426 	    if (lbase) (&slave->newright.x)[!x_dir] = r1 + floor( dist + .5 );
427 	    else (&slave->newleft.x)[!x_dir] = l1 - floor( dist + .5 );
428 	}
429     }
430 }
431 
StemResize(SplineSet * ss,GlyphData * gd,StemData ** dstems,int dcnt,DBounds * orig_b,DBounds * new_b,struct genericchange * genchange,int x_dir)432 static void StemResize( SplineSet *ss,GlyphData *gd, StemData **dstems, int dcnt,
433     DBounds *orig_b, DBounds *new_b, struct genericchange *genchange, int x_dir ) {
434 
435     double stem_scale, cntr_scale, stem_add, cntr_add, stroke_add, cntr_new, width_new;
436     double min_coord = x_dir ? orig_b->minx : orig_b->miny;
437     real *min_new = x_dir ? &new_b->minx : &new_b->miny;
438     real *max_new = x_dir ? &new_b->maxx : &new_b->maxy;
439     real *end, *newstart, *newend, *prevend=NULL, *newprevend=NULL;
440     StemData *stem, *prev=NULL;
441     StemBundle *bundle = x_dir ? gd->vbundle : gd->hbundle;
442     int i, expanded;
443 
444     /* If additional amounts of em units for stem width/height have been
445      * specified, but it was impossible to handle them via BoldSSStroke()
446      * (e. g. because one of two values was zero) then just add them to the
447      * scaled stem width. Otherwise stems have already been expanded, so
448      * there is nothing to add
449      */
450     expanded = (genchange->stem_width_add != 0 && genchange->stem_height_add !=0 &&
451 		genchange->stem_height_add/genchange->stem_width_add > 0 );
452     if ( x_dir ) {
453 	stem_scale = genchange->stem_width_scale;
454 	stem_add = genchange->stem_width_add;
455 	cntr_scale = genchange->hcounter_scale;
456 	cntr_add = genchange->hcounter_add;
457     } else {
458 	stem_scale = genchange->stem_height_scale;
459 	stem_add = genchange->stem_height_add;
460 	cntr_scale = genchange->vcounter_scale;
461 	cntr_add = genchange->vcounter_add;
462     }
463 
464     *min_new = floor( min_coord * cntr_scale + cntr_add + .5 );
465     for ( i=0; i<bundle->cnt; i++ ) {
466 	stem = bundle->stemlist[i];
467 	if ( genchange->stem_threshold > 0 ) {
468 	    stem_scale = stem->width > genchange->stem_threshold ?
469 		genchange->stem_width_scale : genchange->stem_height_scale;
470 	    stem_add = genchange->stem_height_add;
471 	}
472 	stroke_add = expanded ? stem_add : 0;
473 
474 	if ( stem->master == NULL ) {
475 	    newstart = x_dir ? &stem->newleft.x : &stem->newright.x;
476 	    newend = x_dir ? &stem->newright.x : &stem->newleft.x;
477 
478 	    cntr_new = ScaleCounter( gd,dstems,dcnt,orig_b,prev,stem,genchange,x_dir );
479 	    if ( prev == NULL )
480 		newstart[!x_dir] = *min_new + floor( cntr_new + cntr_add + .5 );
481 	    else
482 		newstart[!x_dir] = newprevend[!x_dir] + floor( cntr_new + cntr_add + .5 );
483 
484 	    /* Bounding box hints usually don't mark real stems, so there is */
485 	    /* no reason to maintain the normal stem width ratio when dealing with them */
486 	    if ( stem->bbox )
487 		width_new = ScaleCounter( gd,dstems,dcnt,orig_b,NULL,NULL,genchange,x_dir );
488 	    else
489 		width_new = ( stem->width - stroke_add )*stem_scale + stem_add;
490 	    newend[!x_dir] = newstart[!x_dir] + floor( width_new + .5 );
491 	    stem->ldone = stem->rdone = true;
492 	    StemPosDependent( stem,genchange,x_dir );
493 	    prev = stem;
494 	    newprevend = newend;
495 	}
496     }
497 
498     *max_new = *min_new;
499     prev = NULL;
500     /* A new pass to calculate the glyph's right boundary. Here we */
501     /* need to take into account also dependent stems (and that's why we */
502     /* couldn't do that in the previous cycle) */
503     for ( i=0; i<bundle->cnt; i++ ) {
504 	stem = bundle->stemlist[i];
505 	if ( stem->bbox )
506     continue;
507 	end = x_dir ? &stem->right.x : &stem->left.y;
508 	newend = x_dir ? &stem->newright.x : &stem->newleft.y;
509 	if ( prev == NULL || end[!x_dir] > prevend[!x_dir] ) {
510 	    *max_new = floor( newend[!x_dir] + .5 );
511 	    prevend = end;
512 	    prev = stem;
513 	}
514     }
515     cntr_new = ScaleCounter( gd,dstems,dcnt,orig_b,prev,NULL,genchange,x_dir );
516     *max_new += floor( cntr_new + cntr_add + .5 );
517 }
518 
HStemResize(SplineSet * ss,GlyphData * gd,DBounds * orig_b,DBounds * new_b,struct genericchange * genchange)519 static void HStemResize( SplineSet *ss,GlyphData *gd,
520     DBounds *orig_b, DBounds *new_b, struct genericchange *genchange ) {
521 
522     double middle, scale, stem_scale, stem_add, stroke_add, width_new;
523     double top, bot, lpos, rpos, fuzz = gd->fuzz;
524     StemData *stem, *test, *upper, *lower;
525     int i, j, fcnt, expanded;
526     struct fixed_maps *fix = &genchange->m;
527     struct position_maps *pm;
528 
529     expanded = (genchange->stem_width_add != 0 && genchange->stem_height_add !=0 &&
530 		genchange->stem_height_add/genchange->stem_width_add > 0 );
531     stem_add = genchange->stem_height_add;
532     stroke_add = expanded ? stem_add : 0;
533     scale = genchange->v_scale;
534 
535     new_b->miny = orig_b->miny * scale;
536     new_b->maxy = orig_b->maxy * scale;
537 
538     /* Extend the scaled glyph bounding box if necessary to ensure all mapped values are inside */
539     if ( fix->cnt > 0 ) {
540 	if ( fix->maps[0].cur_width < 0 && new_b->miny > fix->maps[0].desired + fix->maps[0].des_width )
541 	    new_b->miny = fix->maps[0].desired + fix->maps[0].des_width -
542 		genchange->v_scale * ( fix->maps[0].current + fix->maps[0].cur_width - orig_b->miny );
543 	else if ( fix->maps[0].cur_width > 0 && new_b->miny > fix->maps[0].desired )
544 	    new_b->miny = fix->maps[0].desired -
545 		genchange->v_scale * ( fix->maps[0].current - orig_b->miny );
546 
547 	fcnt = fix->cnt - 1;
548 	if ( fix->maps[fcnt].cur_width > 0 && new_b->maxy < fix->maps[fcnt].desired + fix->maps[fcnt].des_width )
549 	    new_b->maxy = fix->maps[fcnt].desired + fix->maps[fcnt].des_width +
550 		genchange->v_scale * ( orig_b->maxy - ( fix->maps[fcnt].current + fix->maps[fcnt].cur_width ));
551 	else if ( fix->maps[fcnt].cur_width < 0 && new_b->maxy < fix->maps[fcnt].desired )
552 	    new_b->maxy = fix->maps[fcnt].desired +
553 		genchange->v_scale * ( orig_b->maxy - fix->maps[fcnt].current );
554     }
555 
556     for ( i=0; i<gd->hbundle->cnt; i++ ) {
557 	stem = gd->hbundle->stemlist[i];
558 	if ( genchange->stem_threshold > 0 )
559 	    stem_scale = stem->width > genchange->stem_threshold ?
560 		genchange->stem_width_scale : genchange->stem_height_scale;
561 	else
562 	    stem_scale = genchange->stem_height_scale;
563 
564 	/* The 'blue' field now actually points to a 'position_maps' structure. */
565 	/* So the name is wrong, but who cares... */
566 	if (stem->blue != -1 ) {
567 	    pm = &fix->maps[stem->blue];
568 	    width_new = ( stem->width - stroke_add )*stem_scale + stem_add;
569 	    lpos = stem->left.y - stroke_add/2;
570 	    rpos = stem->right.y + stroke_add/2;
571 	    if ( pm->cur_width < 0 && ( !stem->ghost || stem->width == 21 ) &&
572 		rpos >= pm->current + pm->cur_width - fuzz && rpos <= pm->current + fuzz ) {
573 
574 		top = pm->current; bot = pm->current + pm->cur_width;
575 		if ( rpos >= bot && rpos <= top )
576 		    stem->newright.y = floor( InterpolateVal( bot,top,
577 			pm->desired + pm->des_width,pm->desired,rpos ) + .5 );
578 		else if ( rpos < bot )
579 		    stem->newright.y = floor( pm->desired + pm->des_width -
580 			( bot - rpos ) * stem_scale + .5 );
581 		else if ( rpos > top )
582 		    stem->newright.y = floor( pm->desired +
583 			( rpos - top ) * stem_scale + .5 );
584 
585 		if ( !stem->ghost )
586 		    stem->newleft.y = stem->newright.y + floor( width_new + .5 );
587 		else
588 		    stem->newleft.y = stem->newright.y + 21;
589 		stem->ldone = stem->rdone = true;
590 	    } else if ( pm->cur_width > 0 && ( !stem->ghost || stem->width == 20 ) &&
591 		lpos >= pm->current - fuzz && lpos <= pm->current + pm->cur_width + fuzz  ) {
592 
593 		top = pm->current + pm->cur_width; bot = pm->current;
594 		if ( lpos >= bot && lpos <= top )
595 		    stem->newleft.y = floor( InterpolateVal( bot,top,
596 			pm->desired,pm->desired + pm->des_width,lpos ) + .5 );
597 		else if ( lpos < bot )
598 		    stem->newleft.y = floor( pm->desired -
599 			( bot - lpos ) * stem_scale + .5 );
600 		else if ( lpos > top )
601 		    stem->newleft.y = floor( pm->desired + pm->des_width +
602 			( lpos - top ) * stem_scale + .5 );
603 
604 		if ( !stem->ghost )
605 		    stem->newright.y = stem->newleft.y - floor( width_new + .5 );
606 		else
607 		    stem->newright.y = stem->newleft.y - 20;
608 		stem->ldone = stem->rdone = true;
609 	    } else
610 		stem->blue = -1;
611 	}
612 	if ( stem->bbox && stem->blue != -1 ) {
613 	    new_b->miny = stem->newright.y;
614 	    new_b->maxy = stem->newleft.y;
615 	}
616     }
617 
618     for ( i=0; i<gd->hbundle->cnt; i++ ) {
619 	stem = gd->hbundle->stemlist[i];
620 	if ( genchange->stem_threshold > 0 )
621 	    stem_scale = stem->width > genchange->stem_threshold ?
622 		genchange->stem_width_scale : genchange->stem_height_scale;
623 	else
624 	    stem_scale = genchange->stem_height_scale;
625 
626 	if (stem->blue == -1 && stem->master == NULL) {
627 	    middle = stem->right.y + stem->width/2;
628 	    upper = lower = NULL;
629 	    width_new = ( stem->width - stroke_add )*stem_scale + stem_add;
630 	    for ( j=0; j<gd->hbundle->cnt; j++ ) {
631 		test = gd->hbundle->stemlist[j];
632 		if ( test != stem && test->blue != -1 ) {
633 		    if ( test->right.y > stem->left.y &&
634 			( upper == NULL || test->right.y < upper->right.y ))
635 			upper = test;
636 		    else if ( test->left.y < stem->right.y &&
637 			( lower == NULL || test->left.y > lower->left.y ))
638 			lower = test;
639 		}
640 	    }
641 	    if ( upper != NULL && lower != NULL ) {
642 		middle = InterpolateVal( lower->left.y,upper->right.y,
643 		    lower->newleft.y,upper->newright.y,middle );
644 		stem->newleft.y = floor( middle + ( width_new/2 + .5 ));
645 		stem->newright.y = floor( middle - ( width_new/2 + .5 ));
646 	    } else if ( upper != NULL ) {
647 		stem->newright.y = floor( InterpolateVal( orig_b->miny,upper->right.y,
648 		    new_b->miny,upper->newright.y,stem->right.y ) + .5 );
649 		stem->newleft.y = stem->newright.y + floor( width_new + .5 );
650 	    } else if ( lower != NULL ) {
651 		stem->newleft.y = floor( InterpolateVal( lower->left.y,orig_b->maxy,
652 		    lower->newleft.y,new_b->maxy,stem->left.y ) + .5 );
653 		stem->newright.y = stem->newleft.y - floor( width_new + .5 );
654 	    } else {
655 		middle = InterpolateVal( orig_b->miny,orig_b->maxy,
656 		    new_b->miny,new_b->maxy,middle );
657 		stem->newleft.y = floor( middle + ( width_new/2 + .5 ));
658 		stem->newright.y = floor( middle - ( width_new/2 + .5 ));
659 	    }
660 	}
661     }
662 
663     for ( i=0; i<gd->hbundle->cnt; i++ ) {
664 	stem = gd->hbundle->stemlist[i];
665 	if ( stem->master == NULL )
666 	    StemPosDependent( stem,genchange,false );
667     }
668     /* Final bounding box adjustment (it is possible that some of the  */
669     /* calculated stem boundaries are outside of the new bounding box, */
670     /* and we should fix this now */
671     upper = lower = NULL;
672     for ( i=0; i<gd->hbundle->cnt; i++ ) {
673 	stem = gd->hbundle->stemlist[i];
674 	if ( upper == NULL || stem->newleft.y > upper->newleft.y )
675 	    upper = stem;
676 	if ( lower == NULL || stem->newright.y < lower->newright.y )
677 	    lower = stem;
678     }
679     if ( upper != NULL )
680 	new_b->maxy = upper->newleft.y + ( orig_b->maxy - upper->left.y )*scale;
681     if ( lower != NULL )
682 	new_b->miny = lower->newright.y - ( lower->right.y - orig_b->miny )*scale;
683 }
684 
InitZoneMappings(struct fixed_maps * fix,BlueData * bd,double stem_scale)685 static void InitZoneMappings( struct fixed_maps *fix, BlueData *bd, double stem_scale ) {
686     int i, j;
687     double s_cur, e_cur, s_des, e_des;
688 
689     /* Sort mappings to ensure they go in the ascending order */
690     qsort( fix->maps,fix->cnt,sizeof( struct position_maps ),fixed_cmp );
691 
692     /* Remove overlapping mappings */
693     for ( i=j=0; i<fix->cnt; i++ ) {
694 	if ( i==j)
695     continue;
696 	s_cur = ( fix->maps[i].cur_width > 0 ) ?
697 	    fix->maps[i].current : fix->maps[i].current + fix->maps[i].cur_width;
698 	e_cur = ( fix->maps[j].cur_width > 0 ) ?
699 	    fix->maps[j].current + fix->maps[j].cur_width : fix->maps[j].current;
700 	s_des = ( fix->maps[i].des_width > 0 ) ?
701 	    fix->maps[i].desired : fix->maps[i].desired + fix->maps[i].des_width;
702 	e_des = ( fix->maps[j].des_width > 0 ) ?
703 	    fix->maps[j].desired + fix->maps[j].des_width : fix->maps[j].desired;
704 	if ( s_cur > e_cur && s_des > e_des ) {
705 	    j++;
706 	    if ( j<i )
707 		memcpy( &fix->maps[j],&fix->maps[i],sizeof( struct position_maps ));
708 	}
709     }
710     if ( i>0 ) fix->cnt = j+1;
711 
712     /* Init a set of blues from user-specified mappings (we need this for */
713     /* GlyphDataBuild(), to get stems associated with blues) */
714     for ( i=0; i<fix->cnt; i++ ) {
715 	fix->maps[i].des_width = floor( fix->maps[i].cur_width * stem_scale + .5 );
716 	if ( i < 12 ) {
717 	    bd->blues[i][0] = fix->maps[i].cur_width < 0 ?
718 		fix->maps[i].current + fix->maps[i].cur_width : fix->maps[i].current;
719 	    bd->blues[i][1] = fix->maps[i].cur_width < 0 ?
720 		fix->maps[i].current : fix->maps[i].current + fix->maps[i].cur_width;
721 	    bd->bluecnt++;
722 	}
723     }
724 }
725 
PosStemPoints(GlyphData * gd,struct genericchange * genchange,int has_dstems,int x_dir)726 static void PosStemPoints( GlyphData *gd, struct genericchange *genchange, int has_dstems, int x_dir ) {
727 
728     int i, j, best_is_l;
729     uint8 flag = x_dir ? tf_x : tf_y;
730     PointData *pd;
731     StemData *tstem, *best;
732     struct stem_chunk *chunk;
733     double dist, bdist, stem_scale;
734     BasePoint *sbase;
735 
736     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
737 	pd = &gd->points[i];
738 	best = NULL; bdist = 0;
739 	for ( j=0; j<pd->prevcnt; j++ ) {
740 	    tstem = pd->prevstems[j];
741 	    if ( !tstem->toobig && tstem->unit.x == !x_dir && RealNear( tstem->unit.y,x_dir )) {
742 		sbase = pd->prev_is_l[j] ? &tstem->left : &tstem->right;
743 		dist = ( pd->base.x - sbase->x )*x_dir - ( pd->base.y - sbase->y )*!x_dir;
744 		if ( best == NULL || fabs( dist ) < fabs( bdist )) {
745 		    best = tstem;
746 		    bdist = dist;
747 		    best_is_l = pd->prev_is_l[j];
748 		}
749 	    }
750 	}
751 	for ( j=0; j<pd->nextcnt; j++ ) {
752 	    tstem = pd->nextstems[j];
753 	    if ( !tstem->toobig && tstem->unit.x == !x_dir && tstem->unit.y == x_dir ) {
754 		sbase = pd->next_is_l[j] ? &tstem->left : &tstem->right;
755 		dist = ( pd->base.x - sbase->x )*x_dir - ( pd->base.y - sbase->y )*!x_dir;
756 		if ( best == NULL || fabs( dist ) < fabs( bdist )) {
757 		    best = tstem;
758 		    bdist = dist;
759 		    best_is_l = pd->next_is_l[j];
760 		}
761 	    }
762 	}
763 
764 	if ( best != NULL ) {
765 	    if ( has_dstems && ( pd->x_corner == 2 || pd->y_corner == 2 )) {
766 		for ( j=0; j<best->chunk_cnt; j++ ) {
767 		    chunk = &best->chunks[j];
768 		    if (( chunk->l == pd || chunk->r == pd ) &&
769 			( chunk->stemcheat == 2 || chunk->stemcheat == 3 ))
770 		break;
771 		}
772 		/* Don't attempt to position inner points at diagonal intersections: */
773 		/* our diagonal stem processor will handle them better */
774 		if ( j<best->chunk_cnt )
775     continue;
776 	    }
777 	    if (genchange->stem_threshold > 0)
778 		stem_scale = best->width > genchange->stem_threshold ?
779 		    genchange->stem_width_scale : genchange->stem_height_scale;
780 	    else
781 		stem_scale = x_dir ? genchange->stem_width_scale : genchange->stem_height_scale;
782 
783 	    if ( !x_dir ) bdist = -bdist;
784 	    (&pd->newpos.x)[!x_dir] = best_is_l ?
785 		(&best->newleft.x)[!x_dir] + bdist * stem_scale :
786 		(&best->newright.x)[!x_dir] + bdist * stem_scale;
787 	    pd->touched |= flag;
788 	    pd->posdir.x = !x_dir; pd->posdir.y = x_dir;
789 	}
790     }
791 }
792 
InterpolateBetweenEdges(GlyphData * gd,double coord,double min,double max,double min_new,double max_new,int x_dir)793 static double InterpolateBetweenEdges( GlyphData *gd, double coord, double min, double max,
794     double min_new, double max_new, int x_dir ) {
795 
796     StemData *stem;
797     StemBundle *bundle = x_dir ? gd->vbundle : gd->hbundle;
798     double prev_pos, next_pos, prev_new, next_new, start, end, ret;
799     int i;
800 
801     prev_pos = -1e4; next_pos = 1e4;
802     for ( i=0; i<bundle->cnt; i++ ) {
803 	stem = bundle->stemlist[i];
804 	start = x_dir ? stem->left.x : stem->right.y;
805 	end = x_dir ? stem->right.x : stem->left.y;
806 
807 	if ( start >= min && start <= max ) {
808 	    if ( start < coord && start > prev_pos ) {
809 		prev_pos = start;
810 		prev_new = x_dir ? stem->newleft.x : stem->newright.y;
811 	    }
812 	    if ( start > coord && start < next_pos ) {
813 		next_pos = start;
814 		next_new = x_dir ? stem->newleft.x : stem->newright.y;
815 	    }
816 	}
817 	if ( end >= min && end <= max ) {
818 	    if ( end > coord && end < next_pos ) {
819 		next_pos = end;
820 		next_new = x_dir ? stem->newright.x : stem->newleft.y;
821 	    }
822 	    if ( end < coord && end > prev_pos ) {
823 		prev_pos = end;
824 		prev_new = x_dir ? stem->newright.x : stem->newleft.y;
825 	    }
826 	}
827     }
828     if ( prev_pos > -1e4 && next_pos < 1e4 )
829 	ret = InterpolateVal( prev_pos,next_pos,prev_new,next_new,coord );
830     else if ( prev_pos > -1e4 )
831 	ret = InterpolateVal( prev_pos,max,prev_new,max_new,coord );
832     else if ( next_pos < 1e4 )
833 	ret = InterpolateVal( min,next_pos,min_new,next_new,coord );
834     else
835 	ret = InterpolateVal( min,max,min_new,max_new,coord );
836 return( ret );
837 }
838 
InterpolateStrong(GlyphData * gd,DBounds * orig_b,DBounds * new_b,int x_dir)839 static void InterpolateStrong( GlyphData *gd, DBounds *orig_b, DBounds *new_b, int x_dir ) {
840     int i;
841     uint8 mask, flag = x_dir ? tf_x : tf_y;
842     double coord, new;
843     double min, max, min_new, max_new;
844     PointData *pd;
845 
846     mask = flag | tf_d;
847     min = x_dir ? orig_b->minx : orig_b->miny;
848     max = x_dir ? orig_b->maxx : orig_b->maxy;
849     min_new = x_dir ? new_b->minx : new_b->miny;
850     max_new = x_dir ? new_b->maxx : new_b->maxy;
851     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
852 	pd = &gd->points[i];
853 
854 	if ( !(pd->touched & mask) && ( IsExtremum(pd->sp,!x_dir) || IsAnglePoint(pd->sp))) {
855 	    coord = (&pd->base.x)[!x_dir];
856 	    new = InterpolateBetweenEdges( gd,coord,min,max,min_new,max_new,x_dir );
857 	    (&pd->newpos.x)[!x_dir] = new;
858 	    pd->touched |= flag;
859 	    pd->posdir.x = !x_dir; pd->posdir.y = x_dir;
860 	}
861     }
862 }
863 
InterpolateWeak(GlyphData * gd,DBounds * orig_b,DBounds * new_b,double scale,int x_dir)864 static void InterpolateWeak( GlyphData *gd, DBounds *orig_b, DBounds *new_b, double scale, int x_dir ) {
865     PointData *pd, *tpd, *fpd;
866     int i;
867     uint8 mask, flag = x_dir ? tf_x : tf_y;
868     double coord, new, min, max, min_new, max_new;
869 
870     mask = flag | tf_d;
871     min = x_dir ? orig_b->minx : orig_b->miny;
872     max = x_dir ? orig_b->maxx : orig_b->maxy;
873     min_new = x_dir ? new_b->minx : new_b->miny;
874     max_new = x_dir ? new_b->maxx : new_b->maxy;
875 
876     /* Position any points which line between points already positioned */
877     for ( i=0; i<gd->pcnt; i++ ) {
878 	pd = &gd->points[i];
879 	if ( pd->sp != NULL && !(pd->touched & mask) ) {
880 	    coord = (&pd->base.x)[!x_dir];
881 	    if ( pd->sp->prev != NULL && pd->sp->next != NULL ) {
882 		fpd = &gd->points[pd->sp->prev->from->ptindex];
883 		while ( !(fpd->touched & mask) && fpd != pd && fpd->sp->prev != NULL )
884 		    fpd = &gd->points[fpd->sp->prev->from->ptindex];
885 
886 		tpd = &gd->points[pd->sp->next->to->ptindex];
887 		while ( !(tpd->touched & mask) && tpd != pd && tpd->sp->next != NULL )
888 		    tpd = &gd->points[tpd->sp->next->to->ptindex];
889 
890 		if (( fpd->touched & mask ) && ( tpd->touched & mask ) &&
891 		    (&fpd->base.x)[!x_dir] != (&tpd->base.x)[!x_dir] && (
892 		    ( (&fpd->base.x)[!x_dir] <= coord && coord <= (&tpd->base.x)[!x_dir] ) ||
893 		    ( (&tpd->base.x)[!x_dir] <= coord && coord <= (&fpd->base.x)[!x_dir] ))) {
894 		    new = InterpolateVal( (&fpd->base.x)[!x_dir],(&tpd->base.x)[!x_dir],
895 					    (&fpd->newpos.x)[!x_dir],(&tpd->newpos.x)[!x_dir],coord );
896 		    (&pd->newpos.x)[!x_dir] = new;
897 		    pd->touched |= flag;
898 		    pd->posdir.x = !x_dir; pd->posdir.y = x_dir;
899 		}
900 	    }
901 	}
902     }
903     /* Any points which aren't currently positioned, just interpolate them */
904     /*  between the hint zones between which they lie */
905     /* I don't think this can actually happen... but do it just in case */
906     for ( i=0; i<gd->pcnt; i++ ) {
907 	pd = &gd->points[i];
908 	if ( pd->sp != NULL && !(pd->touched & mask) ) {
909 	    coord = (&pd->base.x)[!x_dir];
910 	    new = InterpolateBetweenEdges( gd,coord,min,max,min_new,max_new,x_dir );
911 	    (&pd->newpos.x)[!x_dir] = new;
912 	    pd->touched |= flag;
913 	    pd->posdir.x = !x_dir; pd->posdir.y = x_dir;
914 	}
915     }
916 
917     /* Interpolate the control points. More complex in order2. We want to */
918     /*  preserve interpolated points, but simplified as we only have one cp */
919     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
920 	pd = &gd->points[i];
921 	if ( !pd->sp->noprevcp && !gd->order2 ) {
922 	    coord = (&pd->sp->prevcp.x)[!x_dir];
923 	    fpd = &gd->points[pd->sp->prev->from->ptindex];
924 	    if (coord == (&pd->base.x)[!x_dir] )
925 		(&pd->newprev.x)[!x_dir] = (&pd->newpos.x)[!x_dir];
926 	    else {
927 		if (( coord >= (&fpd->base.x)[!x_dir] && coord <= (&pd->base.x)[!x_dir] ) ||
928 		    ( coord <= (&fpd->base.x)[!x_dir] && coord >= (&pd->base.x)[!x_dir] ))
929 
930 		    (&pd->newprev.x)[!x_dir] = InterpolateVal((&pd->base.x)[!x_dir],
931 			(&fpd->base.x)[!x_dir],(&pd->newpos.x)[!x_dir],(&fpd->newpos.x)[!x_dir],coord);
932 		else
933 		    (&pd->newprev.x)[!x_dir] = (&pd->newpos.x)[!x_dir] +
934 			( coord - (&pd->base.x)[!x_dir] )*scale;
935 	    }
936 	}
937 	if ( !pd->sp->nonextcp ) {
938 	    coord = (&pd->sp->nextcp.x)[!x_dir];
939 	    tpd = &gd->points[pd->sp->next->to->ptindex];
940 	    if ( coord == (&pd->base.x)[!x_dir] )
941 		(&pd->newnext.x)[!x_dir] = (&pd->newpos.x)[!x_dir];
942 	    else {
943 		if (( coord >= (&tpd->base.x)[!x_dir] && coord <= (&pd->base.x)[!x_dir] ) ||
944 		    ( coord <= (&tpd->base.x)[!x_dir] && coord >= (&pd->base.x)[!x_dir] ))
945 
946 		    (&pd->newnext.x)[!x_dir] = InterpolateVal((&pd->base.x)[!x_dir],
947 			(&tpd->base.x)[!x_dir],(&pd->newpos.x)[!x_dir],(&tpd->newpos.x)[!x_dir],coord);
948 		else
949 		    (&pd->newnext.x)[!x_dir] = (&pd->newpos.x)[!x_dir] +
950 			( coord - (&pd->base.x)[!x_dir] )*scale;
951 	    }
952 	    if ( gd->order2 )
953 	       (&tpd->newprev.x)[!x_dir] = (&pd->newnext.x)[!x_dir];
954 	}
955     }
956     if ( gd->order2 ) {
957 	for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
958 	    pd = &gd->points[i];
959 	    if ( pd->ttfindex == 0xFFFF ) {
960 		(&pd->newpos.x)[!x_dir] = ((&pd->newnext.x)[!x_dir] + (&pd->newprev.x)[!x_dir])/2;
961 	    }
962 	}
963     }
964 }
965 
InterpolateAnchorPoints(GlyphData * gd,AnchorPoint * aps,DBounds * orig_b,DBounds * new_b,double scale,int x_dir)966 static void InterpolateAnchorPoints( GlyphData *gd,AnchorPoint *aps,
967     DBounds *orig_b, DBounds *new_b,double scale,int x_dir ) {
968 
969     AnchorPoint *ap;
970     double coord, new, min, max, min_new, max_new;
971 
972     min = x_dir ? orig_b->minx : orig_b->miny;
973     max = x_dir ? orig_b->maxx : orig_b->maxy;
974     min_new = x_dir ? new_b->minx : new_b->miny;
975     max_new = x_dir ? new_b->maxx : new_b->maxy;
976 
977     for ( ap=aps; ap!=NULL; ap=ap->next ) {
978 	coord = (&ap->me.x)[!x_dir];
979 	/* Anchor points might be outside the bounding box */
980 	if ( coord >= min && coord <= max )
981 	    new = InterpolateBetweenEdges( gd,coord,min,max,min_new,max_new,x_dir );
982 	else if ( coord < min )
983 	    new = min_new - ( min - coord ) * scale;
984 	else
985 	    new = max_new + ( coord - max ) * scale;
986 
987 	(&ap->me.x)[!x_dir] = new;
988     }
989 }
990 
PrepareDStemList(GlyphData * gd,StemData ** dstems)991 static int PrepareDStemList( GlyphData *gd, StemData **dstems ) {
992     double lpos, rpos, prevlsp, prevrsp, prevlep, prevrep;
993     PointData *ls, *rs, *le, *re;
994     struct stem_chunk *chunk;
995     StemData *stem;
996     int i, j, dcnt=0;
997 
998     for ( i=0; i<gd->stemcnt; i++ ) {
999 	stem = &gd->stems[i];
1000 	if ( stem->toobig ||
1001 	    ( stem->unit.y > -.05 && stem->unit.y < .05 ) ||
1002 	    ( stem->unit.x > -.05 && stem->unit.x < .05 ) ||
1003 	    stem->activecnt == 0 || stem->lpcnt == 0 || stem->rpcnt == 0 )
1004     continue;
1005 
1006 	prevlsp = prevrsp = 1e4;
1007 	prevlep = prevrep = -1e4;
1008 	ls = rs = le = re = NULL;
1009 	for ( j=0; j<stem->chunk_cnt; j++ ) {
1010 	    chunk = &stem->chunks[j];
1011 	    if ( chunk->l != NULL ) {
1012 		lpos =  ( chunk->l->base.x - stem->left.x )*stem->unit.x +
1013 			( chunk->l->base.y - stem->left.y )*stem->unit.y;
1014 		if ( lpos < prevlsp ) {
1015 		    ls = chunk->l; prevlsp = lpos;
1016 		}
1017 		if ( lpos > prevlep ) {
1018 		    le = chunk->l; prevlep = lpos;
1019 		}
1020 	    }
1021 	    if ( chunk->r != NULL ) {
1022 		rpos =  ( chunk->r->base.x - stem->right.x )*stem->unit.x +
1023 			( chunk->r->base.y - stem->right.y )*stem->unit.y;
1024 		if ( rpos < prevrsp ) {
1025 		    rs = chunk->r; prevrsp = rpos;
1026 		}
1027 		if ( rpos > prevrep ) {
1028 		    re = chunk->r; prevrep = rpos;
1029 		}
1030 	   }
1031 	}
1032 	if ( ls == NULL || rs == NULL || le == NULL || re == NULL )
1033     continue;
1034 	stem->keypts[0] = ls; stem->keypts[1] = le;
1035 	stem->keypts[2] = rs; stem->keypts[3] = re;
1036 	dstems[dcnt++] = stem;
1037     }
1038     qsort( dstems,dcnt,sizeof( StemData *),ds_cmp );
1039 return( dcnt );
1040 }
1041 
MovePointToDiag(PointData * pd,StemData * stem,int is_l)1042 static void MovePointToDiag( PointData *pd, StemData *stem, int is_l ) {
1043     BasePoint fv, *base, *ptpos;
1044     double d, da;
1045 
1046     base = is_l ? &stem->newleft : &stem->newright;
1047     if ( !pd->touched ) {
1048 	ptpos = &pd->base;
1049 	if ( fabs( stem->unit.x ) > fabs( stem->unit.y )) {
1050 	    fv.x = 0; fv.y = 1;
1051 	} else {
1052 	    fv.x = 1; fv.y = 0;
1053 	}
1054     } else {
1055 	fv.x = pd->posdir.x; fv.y = pd->posdir.y;
1056 	ptpos = &pd->newpos;
1057     }
1058 
1059     d = stem->newunit.x * fv.y - stem->newunit.y * fv.x;
1060     da = ( base->x - ptpos->x )*fv.y - ( base->y - ptpos->y )*fv.x;
1061 
1062     if ( fabs( d ) > 0 ) {
1063 	pd->newpos.x = base->x - ( da/d )*stem->newunit.x;
1064 	pd->newpos.y = base->y - ( da/d )*stem->newunit.y;
1065 	if ( pd->touched & tf_d && ( pd->posdir.x != stem->newunit.x || pd->posdir.y != stem->newunit.y )) {
1066 	    pd->touched |= tf_x; pd->touched |= tf_y;
1067 	} else
1068 	    pd->touched |= tf_d;
1069 	pd->posdir.x = stem->newunit.x; pd->posdir.y = stem->newunit.y;
1070     }
1071 }
1072 
GetDStemBounds(GlyphData * gd,StemData * stem,real * prev,real * next,int x_dir)1073 static void GetDStemBounds( GlyphData *gd, StemData *stem, real *prev, real *next, int x_dir ) {
1074     int i, maxact;
1075     double roff, dstart, dend, hvstart, hvend, temp;
1076     StemBundle *bundle;
1077     StemData *hvstem;
1078 
1079     roff =  ( stem->right.x - stem->left.x ) * stem->unit.x +
1080 	    ( stem->right.y - stem->left.y ) * stem->unit.y;
1081     maxact = stem->activecnt - 1;
1082 
1083     if ( stem->unit.y > 0 ) {
1084 	dstart = x_dir ?
1085 	    stem->right.x + ( stem->active[0].start - roff ) * stem->unit.x :
1086 	    stem->left.y + stem->active[0].start * stem->unit.y;
1087 	dend = x_dir ?
1088 	    stem->left.x + stem->active[maxact].end * stem->unit.x :
1089 	    stem->right.y + ( stem->active[maxact].end - roff ) * stem->unit.y;
1090     } else {
1091 	dstart = x_dir ?
1092 	    stem->left.x + stem->active[0].start * stem->unit.x :
1093 	    stem->right.y + ( stem->active[0].start - roff ) * stem->unit.y ;
1094 	dend = x_dir ?
1095 	    stem->right.x + ( stem->active[maxact].end - roff ) * stem->unit.x :
1096 	    stem->left.y + stem->active[maxact].end * stem->unit.y;
1097     }
1098     if ( dstart > dend ) {
1099 	temp = dstart; dstart = dend; dend = temp;
1100     }
1101 
1102     bundle = x_dir ? gd->vbundle : gd->hbundle;
1103     for ( i=0; i<bundle->cnt; i++ ) {
1104 	hvstem = bundle->stemlist[i];
1105 	hvstart = x_dir ? hvstem->left.x : hvstem->right.y;
1106 	hvend = x_dir ? hvstem->right.x : hvstem->left.y;
1107 	if ( hvend > *prev && hvend <= dstart )
1108 	    *prev = hvend;
1109 	else if ( hvstart < *next && hvstart >= dend )
1110 	    *next = hvstart;
1111     }
1112 }
1113 
AlignPointPair(StemData * stem,PointData * lpd,PointData * rpd,double hscale,double vscale)1114 static void AlignPointPair( StemData *stem,PointData *lpd, PointData *rpd, double hscale,double vscale ) {
1115     double off, newoff, dscale;
1116 
1117     /* If points are already horizontally or vertically aligned, */
1118     /* then there is nothing more to do here */
1119     if (( lpd->base.x == rpd->base.x && lpd->newpos.x == rpd->newpos.x ) ||
1120 	( lpd->base.y == rpd->base.y && lpd->newpos.y == rpd->newpos.y ))
1121 return;
1122 
1123     dscale =  sqrt( pow( hscale * stem->unit.x,2 ) +
1124 		    pow( vscale * stem->unit.y,2 ));
1125     if ( !IsPointFixed( rpd )) {
1126 	 off    =( rpd->base.x - lpd->base.x ) * stem->unit.x +
1127 		 ( rpd->base.y - lpd->base.y ) * stem->unit.y;
1128 	 newoff =( rpd->newpos.x - lpd->newpos.x ) * stem->newunit.x +
1129 		 ( rpd->newpos.y - lpd->newpos.y ) * stem->newunit.y;
1130 	 rpd->newpos.x += ( off*dscale - newoff )*stem->newunit.x;
1131 	 rpd->newpos.y += ( off*dscale - newoff )*stem->newunit.y;
1132      } else if ( !IsPointFixed( lpd )) {
1133 	 off    =( lpd->base.x - rpd->base.x ) * stem->unit.x +
1134 		 ( lpd->base.y - rpd->base.y ) * stem->unit.y;
1135 	 newoff =( lpd->newpos.x - rpd->newpos.x ) * stem->newunit.x +
1136 		 ( lpd->newpos.y - rpd->newpos.y ) * stem->newunit.y;
1137 	 lpd->newpos.x += ( off*dscale - newoff )*stem->newunit.x;
1138 	 lpd->newpos.y += ( off*dscale - newoff )*stem->newunit.y;
1139      }
1140 }
1141 
CorrectDPointPos(GlyphData * gd,PointData * pd,StemData * stem,double scale,int next,int is_l,int x_dir)1142 static int CorrectDPointPos( GlyphData *gd, PointData *pd, StemData *stem,
1143     double scale, int next, int is_l, int x_dir ) {
1144 
1145     int i, found = 0;
1146     uint8 flag = x_dir ? tf_x : tf_y;
1147     double ndot, pdot, coord_orig, coord_new, base_orig, base_new;
1148     Spline *ns;
1149     StemData *tstem;
1150     PointData *npd;
1151 
1152     if ( IsPointFixed( pd ))
1153 return( false );
1154     ns = next ? pd->sp->next : pd->sp->prev;
1155     if ( ns == NULL )
1156 return( false );
1157     npd = next ? &gd->points[ns->to->ptindex] : &gd->points[ns->from->ptindex];
1158     if ( IsStemAssignedToPoint( npd,stem,!next ) != -1 )
1159 return( false );
1160     ndot = pd->nextunit.x * npd->nextunit.x + pd->nextunit.y * npd->nextunit.y;
1161     pdot = pd->prevunit.x * npd->prevunit.x + pd->prevunit.y * npd->prevunit.y;
1162 
1163     while ( npd != pd && ( ndot > 0 || pdot > 0 )) {
1164 	if ( npd->touched & flag ) {
1165 	    for ( i=0; i<npd->prevcnt && !found; i++ ) {
1166 		tstem = npd->prevstems[i];
1167 		if ( !tstem->toobig && tstem->unit.x == !x_dir && tstem->unit.y == x_dir )
1168 		    found = true;
1169 	    }
1170 	    for ( i=0; i<npd->nextcnt && !found; i++ ) {
1171 		tstem = npd->nextstems[i];
1172 		if ( !tstem->toobig && tstem->unit.x == !x_dir && tstem->unit.y == x_dir )
1173 		    found = true;
1174 	    }
1175 	}
1176 	if ( found )
1177     break;
1178 	ns = next ? npd->sp->next : npd->sp->prev;
1179 	if ( ns == NULL )
1180     break;
1181 	npd = next ? &gd->points[ns->to->ptindex] : &gd->points[ns->from->ptindex];
1182 	ndot = pd->nextunit.x * npd->nextunit.x + pd->nextunit.y * npd->nextunit.y;
1183 	pdot = pd->prevunit.x * npd->prevunit.x + pd->prevunit.y * npd->prevunit.y;
1184     }
1185     if ( !found )
1186 return( false );
1187 
1188     coord_orig = (&pd->base.x)[!x_dir];
1189     coord_new = (&pd->newpos.x)[!x_dir];
1190     base_orig = (&npd->base.x)[!x_dir];
1191     base_new = (&npd->newpos.x)[!x_dir];
1192     if (( coord_orig > base_orig && coord_new <= base_new ) ||
1193 	( coord_orig < base_orig && coord_new >= base_new )) {
1194 	coord_new = base_new + ( coord_orig - base_orig )*scale;
1195 	if ( x_dir ) {
1196 	    pd->newpos.y += stem->newunit.y * (( coord_new - pd->newpos.x  )/stem->newunit.x );
1197 	    pd->newpos.x = coord_new;
1198 	} else {
1199 	    pd->newpos.x += stem->newunit.x * (( coord_new - pd->newpos.y )/stem->newunit.y );
1200 	    pd->newpos.y = coord_new;
1201 	}
1202 return( true );
1203     }
1204 return( false );
1205 }
1206 
ShiftDependent(GlyphData * gd,PointData * pd,StemData * stem,DBounds * orig_b,DBounds * new_b,double cscale,int next,int is_l,int x_dir)1207 static void ShiftDependent( GlyphData *gd, PointData *pd, StemData *stem,
1208     DBounds *orig_b, DBounds *new_b, double cscale, int next, int is_l, int x_dir ) {
1209 
1210     uint8 flag = x_dir ? tf_x : tf_y;
1211     int i, scnt, from_min;
1212     double ndot, pdot, off, dist;
1213     Spline *ns;
1214     PointData *npd;
1215     StemData *tstem, *dstem = NULL;
1216 
1217     /* Check if the given side of the point is controlled by another */
1218     /* DStem. If so, then we should check against that stem instead  */
1219     scnt = next ? pd->nextcnt : pd->prevcnt;
1220     for ( i=0; i<scnt; i++ ) {
1221 	tstem = next ? pd->nextstems[i] : pd->prevstems[i];
1222 	if ( tstem != stem && !tstem->toobig &&
1223 	    ( tstem->unit.x < -.05 || tstem->unit.x > .05 ) &&
1224 	    ( tstem->unit.y < -.05 || tstem->unit.y > .05 ))
1225 return;
1226     }
1227 
1228     /* Is this edge closer to the initial or the final coordinate of the bounding box? */
1229     from_min = (( x_dir && is_l && stem->unit.y > 0 ) ||
1230 		( x_dir && !is_l && stem->unit.y < 0 ) ||
1231 		( !x_dir && !is_l ));
1232     ns = next ? pd->sp->next : pd->sp->prev;
1233     if ( ns == NULL )
1234 return;
1235     npd = next ? &gd->points[ns->to->ptindex] : &gd->points[ns->from->ptindex];
1236     if ( IsStemAssignedToPoint( npd,stem,!next ) != -1 )
1237 return;
1238     ndot = pd->nextunit.x * npd->nextunit.x + pd->nextunit.y * npd->nextunit.y;
1239     pdot = pd->prevunit.x * npd->prevunit.x + pd->prevunit.y * npd->prevunit.y;
1240     off =   ( npd->base.x - pd->base.x )*stem->unit.y -
1241 	    ( npd->base.y - pd->base.y )*stem->unit.x;
1242     dist =  (&npd->base.x)[!x_dir] - (&pd->base.x)[!x_dir];
1243 
1244     while ( npd != pd && ( ndot > 0 || pdot > 0 ) && !( npd->touched & flag ) && (
1245 	( is_l && off <= 0 ) || ( !is_l && off >= 0 ) ||
1246 	( from_min && dist <= 0 ) || ( !from_min && dist >= 0 ))) {
1247 
1248 	/* Can't just check if the point is touched in a diagonal direction,
1249 	 * since not all DStems may have been done at the moment. So see if there
1250 	 * are any valid DStems attached to the point. If so, then it is not
1251 	 * a candidate for the strong interpolation, and thus there is no need
1252 	 * to adjust its position here
1253 	 */
1254 	for ( i=0; i<npd->prevcnt && dstem == NULL; i++ ) {
1255 	    tstem = npd->prevstems[i];
1256 	    if ( !tstem->toobig &&
1257 		( tstem->unit.x < -.05 || tstem->unit.x > .05 ) &&
1258 		( tstem->unit.y < -.05 || tstem->unit.y > .05 ))
1259 		dstem = tstem;
1260 	}
1261 	for ( i=0; i<npd->nextcnt && dstem == NULL; i++ ) {
1262 	    tstem = npd->nextstems[i];
1263 	    if ( !tstem->toobig &&
1264 		( tstem->unit.x < -.05 || tstem->unit.x > .05 ) &&
1265 		( tstem->unit.y < -.05 || tstem->unit.y > .05 ))
1266 		dstem = tstem;
1267 	}
1268 	if ( dstem != NULL )
1269     break;
1270 	if ( IsExtremum( npd->sp,!x_dir ) || IsAnglePoint( npd->sp )) {
1271 	    (&npd->newpos.x)[!x_dir] = (&pd->newpos.x)[!x_dir] +
1272 		((&npd->base.x)[!x_dir] - (&pd->base.x)[!x_dir]) * cscale;
1273 
1274 	    npd->touched |= flag;
1275 	    npd->posdir.x = !x_dir; npd->posdir.y = x_dir;
1276 	}
1277 
1278 	ns = next ? npd->sp->next : npd->sp->prev;
1279 	if ( ns == NULL )
1280     break;
1281 	npd = next ? &gd->points[ns->to->ptindex] : &gd->points[ns->from->ptindex];
1282 	ndot = pd->nextunit.x * npd->nextunit.x + pd->nextunit.y * npd->nextunit.y;
1283 	pdot = pd->prevunit.x * npd->prevunit.x + pd->prevunit.y * npd->prevunit.y;
1284 	off =   ( npd->base.x - pd->base.x )*stem->unit.y -
1285 		( npd->base.y - pd->base.y )*stem->unit.x;
1286 	dist =  (&npd->base.x)[!x_dir] - (&pd->base.x)[!x_dir];
1287     }
1288 }
1289 
1290 /* If several DStems share one common edge, then use the longer one to
1291  * position point on that edge and ignore others. Otherwise we can go
1292  * far outside of our bounding box, attempting to find an intersection
1293  * of two almost parallel (but still different) vectors
1294  */
PreferredDStem(PointData * pd,StemData * stem,int next)1295 static int PreferredDStem( PointData *pd, StemData *stem, int next ) {
1296     int i, stemcnt = next ? pd->nextcnt : pd->prevcnt;
1297     StemData *tstem;
1298 
1299     for ( i=0; i<stemcnt; i++ ) {
1300 	tstem = next ? pd->nextstems[i] : pd->prevstems[i];
1301 	if ( tstem != stem && !tstem->toobig &&
1302 	    ( tstem->unit.y < -.05 || tstem->unit.y > .05 ) &&
1303 	    ( tstem->unit.x < -.05 || tstem->unit.x > .05 ) && tstem->clen > stem->clen )
1304 return( false );
1305     }
1306 return( true );
1307 }
1308 
FixDStem(GlyphData * gd,StemData * stem,StemData ** dstems,int dcnt,DBounds * orig_b,DBounds * new_b,struct genericchange * genchange)1309 static void FixDStem( GlyphData *gd, StemData *stem,  StemData **dstems, int dcnt,
1310     DBounds *orig_b, DBounds *new_b, struct genericchange *genchange ) {
1311 
1312     int i, is_l, pref_y, nextidx, previdx;
1313     PointData *pd, *lfixed=NULL, *rfixed=NULL;
1314     double new_hyp, new_w, des_w, hscale, vscale, hscale1, vscale1, cscale;
1315     double coord_new, min, max, min_new, max_new;
1316     BasePoint l_to_r, left, right, temp;
1317     DBounds stem_b;
1318     StemData *hvstem;
1319 
1320     /* Find the ratio by which the stem is going to be downsized in both directions.
1321      * To do this, we assume each DStem crosses a counter and calculate the ratio by
1322      * comparing the original and resulting size of that counter. For "A" or "V"
1323      * such a "counter" will most probably cover the overall glyph width, while for
1324      * "M" or "N" it will be limited to the space between two vertical stems.
1325      * For the vertical direction we could just use genchange->v_scale instead, but it
1326      * is not guaranteed to be relevant if some blues were mapped to positions,
1327      * different from their default (scaled) values
1328      */
1329     stem_b.minx = orig_b->minx; stem_b.maxx = orig_b->maxx;
1330     GetDStemBounds( gd,stem,&stem_b.minx,&stem_b.maxx,true );
1331     min = orig_b->minx; max = orig_b->maxx;
1332     min_new = new_b->minx; max_new = new_b->maxx;
1333     for ( i=0; i<gd->vbundle->cnt; i++ ) {
1334 	hvstem = gd->vbundle->stemlist[i];
1335 	if (( RealNear( hvstem->left.x,min ) && RealNear( hvstem->right.x,max )) ||
1336 	    ( RealNear( hvstem->left.x,max ) && RealNear( hvstem->right.x,min )))
1337     continue;
1338 	if ( hvstem->left.x >= stem_b.maxx && hvstem->left.x < max ) {
1339 	    max = hvstem->left.x;
1340 	    max_new = hvstem->newleft.x;
1341 	} else if ( hvstem->right.x <= stem_b.minx && hvstem->right.x > min ) {
1342 	    min = hvstem->right.x;
1343 	    min_new = hvstem->newright.x;
1344 	}
1345     }
1346     if ( max == min ) {
1347 	stem->toobig = true;
1348 return;
1349     }
1350     hscale = ( max_new - min_new )/( max - min );
1351 
1352     stem_b.miny = orig_b->miny; stem_b.maxy = orig_b->maxy;
1353     GetDStemBounds( gd,stem,&stem_b.miny,&stem_b.maxy,false );
1354     min = orig_b->miny; max = orig_b->maxy;
1355     min_new = new_b->miny; max_new = new_b->maxy;
1356     for ( i=0; i<gd->hbundle->cnt; i++ ) {
1357 	hvstem = gd->hbundle->stemlist[i];
1358 	if ( hvstem->left.y == min && hvstem->right.y == max )
1359     continue;
1360 	if ( hvstem->right.y >= stem_b.maxy && hvstem->right.y < max ) {
1361 	    max = hvstem->right.y;
1362 	    max_new = hvstem->newright.y;
1363 	} else if ( hvstem->left.y <= stem_b.miny && hvstem->left.y > min ) {
1364 	    min = hvstem->left.y;
1365 	    min_new = hvstem->newleft.y;
1366 	}
1367     }
1368     if ( max == min ) {
1369 	stem->toobig = true;
1370 return;
1371     }
1372     vscale = ( max_new - min_new )/( max - min );
1373 
1374     /* Now scale positions of the left and right virtual points on the stem */
1375     /* by the ratios used to resize horizontal and vertical stems. We need  */
1376     /* this solely to calculate the desired stem width */
1377     if (genchange->stem_threshold > 0) {
1378 	hscale1 = vscale1 = stem->width > genchange->stem_threshold ?
1379 	    genchange->stem_width_scale : genchange->stem_height_scale;
1380     } else {
1381 	hscale1 = genchange->stem_height_scale;
1382 	vscale1 = genchange->stem_width_scale;
1383     }
1384     stem->newleft.x = stem->left.x * vscale1;
1385     stem->newleft.y = stem->left.y * hscale1;
1386     stem->newright.x = stem->right.x * vscale1;
1387     stem->newright.y = stem->right.y * hscale1;
1388     stem->newunit.x = stem->unit.x * vscale1;
1389     stem->newunit.y = stem->unit.y * hscale1;
1390     new_hyp = sqrt( pow( stem->newunit.x,2 ) + pow( stem->newunit.y,2 ));
1391     stem->newunit.x /= new_hyp;
1392     stem->newunit.y /= new_hyp;
1393 
1394     des_w = ( stem->newright.x - stem->newleft.x ) * stem->newunit.y -
1395 	    ( stem->newright.y - stem->newleft.y ) * stem->newunit.x;
1396 
1397     /* For italic stems we move left and right coordinates to the baseline, so   */
1398     /* that they may well fall outside of the bounding box. We have to take care */
1399     /* of this situation */
1400     left = stem->left; right = stem->right;
1401     if ( left.x < orig_b->minx ) {
1402 	left.y += (( orig_b->minx - left.x ) * stem->unit.y )/stem->unit.x;
1403 	left.x = orig_b->minx;
1404     }
1405     if ( right.x < orig_b->minx ) {
1406 	right.y += (( orig_b->minx - right.x ) * stem->unit.y )/stem->unit.x;
1407 	right.x = orig_b->minx;
1408     }
1409 
1410     /* OK, now we know the desired width, so interpolate the coordinates of our
1411      * left and right points to determine where our DStem would probably be
1412      * placed if there were no DStem processor. We are not expecting to get an
1413      * exact result here, but it still would be a good starting point
1414      */
1415     stem->newleft.x = InterpolateBetweenEdges(
1416 	gd,left.x,orig_b->minx,orig_b->maxx,new_b->minx,new_b->maxx,true );
1417     stem->newleft.y = InterpolateBetweenEdges(
1418 	gd,left.y,orig_b->miny,orig_b->maxy,new_b->miny,new_b->maxy,false );
1419     stem->newright.x = InterpolateBetweenEdges(
1420 	gd,right.x,orig_b->minx,orig_b->maxx,new_b->minx,new_b->maxx,true );
1421     stem->newright.y = InterpolateBetweenEdges(
1422 	gd,right.y,orig_b->miny,orig_b->maxy,new_b->miny,new_b->maxy,false );
1423 
1424     /* Adjust the stem unit vector according to the horizontal and vertical */
1425     /* ratios we have previously determined */
1426     if ( stem->bundle != NULL && stem->bundle == gd->ibundle ) {
1427 	stem->newunit.x = stem->unit.x;
1428 	stem->newunit.y = stem->unit.y;
1429     } else {
1430 	stem->newunit.x = stem->unit.x * hscale;
1431 	stem->newunit.y = stem->unit.y * vscale;
1432 	new_hyp = sqrt( pow( stem->newunit.x,2 ) + pow( stem->newunit.y,2 ));
1433 	stem->newunit.x /= new_hyp;
1434 	stem->newunit.y /= new_hyp;
1435     }
1436 
1437     /* Get a possible width of the stem in the scaled glyph. We are going to */
1438     /* adjust that width then */
1439     new_w = ( stem->newright.x - stem->newleft.x ) * stem->newunit.y -
1440 	    ( stem->newright.y - stem->newleft.y ) * stem->newunit.x;
1441     if ( new_w < 0 ) {
1442 	temp.x = stem->newleft.x; temp.y = stem->newleft.y;
1443 	stem->newleft.x = stem->newright.x; stem->newleft.y = stem->newright.y;
1444 	stem->newright.x = temp.x; stem->newright.y = temp.y;
1445 	new_w = -new_w;
1446     }
1447     /* Guess at which normal we want */
1448     l_to_r.x = stem->newunit.y; l_to_r.y = -stem->newunit.x;
1449     /* If we guessed wrong, use the other */
1450     if (( stem->newright.x - stem->newleft.x )*l_to_r.x +
1451 	( stem->newright.y - stem->newleft.y )*l_to_r.y < 0 ) {
1452 	l_to_r.x = -l_to_r.x;
1453 	l_to_r.y = -l_to_r.y;
1454     }
1455 
1456     /* Now look if there are any points on the edges of our stem which are already fixed */
1457     /* both by x and y and thus can no longer be moved */
1458     for ( i=0; i<gd->pcnt && ( lfixed == NULL || rfixed == NULL ); i++ ) if ( gd->points[i].sp != NULL ) {
1459 	pd = &gd->points[i];
1460 	nextidx = IsStemAssignedToPoint( pd,stem,true );
1461 	previdx = IsStemAssignedToPoint( pd,stem,false );
1462 	if (( nextidx != -1 || previdx != -1 ) && IsPointFixed( pd )) {
1463 	    is_l = ( nextidx == -1 ) ? pd->prev_is_l[previdx] : pd->next_is_l[nextidx];
1464 	    if ( is_l ) lfixed = pd;
1465 	    else rfixed = pd;
1466 	}
1467     }
1468     /* If there are such points at both edges, then we probably can do nothing useful */
1469     /* with this stem */
1470     if ( lfixed != NULL && rfixed != NULL ) {
1471 	stem->toobig = true;
1472 return;
1473     }
1474     /* If just one edge is fixed, then use it as a base and move another edge to */
1475     /* maintain the desired stem width */
1476     else if ( lfixed != NULL ) {
1477 	stem->newleft.x = lfixed->newpos.x; stem->newleft.y = lfixed->newpos.y;
1478 	stem->newright.x = stem->newleft.x + des_w * l_to_r.x;
1479 	stem->newright.y = stem->newleft.y + des_w * l_to_r.y;
1480     } else if ( rfixed != NULL ) {
1481 	stem->newright.x = rfixed->newpos.x; stem->newright.y = rfixed->newpos.y;
1482 	stem->newleft.x = stem->newright.x - des_w * l_to_r.x;
1483 	stem->newleft.y = stem->newright.y - des_w * l_to_r.y;
1484     /* Otherwise move both edges to an equal amount of space from their initial */
1485     /* positions */
1486     } else {
1487 	stem->newleft.x = stem->newleft.x - ( des_w - new_w )/2 * l_to_r.x;
1488 	stem->newleft.y = stem->newleft.y - ( des_w - new_w )/2 * l_to_r.y;
1489 	stem->newright.x = stem->newright.x + ( des_w - new_w )/2 * l_to_r.x;
1490 	stem->newright.y = stem->newright.y + ( des_w - new_w )/2 * l_to_r.y;
1491     }
1492 
1493     /* Determine the preferred direction for moving points which have not */
1494     /* already been touched */
1495     pref_y = fabs( stem->unit.y ) > fabs( stem->unit.x );
1496     min = pref_y ? orig_b->miny : orig_b->minx;
1497     max = pref_y ? orig_b->maxy : orig_b->maxx;
1498     min_new = pref_y ? new_b->miny : new_b->minx;
1499     max_new = pref_y ? new_b->maxy : new_b->maxx;
1500 
1501     /* Now proceed to positioning points */
1502     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
1503 	pd = &gd->points[i];
1504 	nextidx = IsStemAssignedToPoint( pd,stem,true );
1505 	previdx = IsStemAssignedToPoint( pd,stem,false );
1506 	if (( nextidx == -1 && previdx == -1 ) || IsPointFixed( pd ))
1507     continue;
1508 	if (( nextidx != -1 && !PreferredDStem( pd,stem,true)) ||
1509 	    ( previdx != -1 && !PreferredDStem( pd,stem,false)))
1510     continue;
1511 	is_l = ( nextidx == -1 ) ? pd->prev_is_l[previdx] : pd->next_is_l[nextidx];
1512 	/* Move the point to a diagonal line. This doesn't yes guarantees it will */
1513 	/* be placed inside our bounding box */
1514 	MovePointToDiag( pd,stem,is_l );
1515 
1516 	if ( !IsPointFixed( pd )) {
1517 	    /* Interpolate the point between either horizontal or vertical stem
1518 	     * edges along the preferred direction (determined according to the stem
1519 	     * unit vector). This will position points still floating outside the bounding
1520 	     * box and also guarantees e. g. proper point positioning relatively to serifs
1521 	     * if they have been expanded
1522 	     */
1523 	    coord_new = InterpolateBetweenEdges(
1524 		gd,(&pd->base.x)[pref_y],min,max,min_new,max_new,!pref_y );
1525 	    if ( pref_y ) {
1526 		pd->newpos.x += stem->newunit.x * (( coord_new - pd->newpos.y )/stem->newunit.y );
1527 		pd->newpos.y = coord_new;
1528 	    } else {
1529 		pd->newpos.y += stem->newunit.y * (( coord_new - pd->newpos.x  )/stem->newunit.x );
1530 		pd->newpos.x = coord_new;
1531 	    }
1532 
1533 	 }
1534 	/* Check if there are obvious displacements relatively to stems going in the
1535 	 * other direction and correct them. For example, the top left point of the diagonal
1536 	 * stem in a Latin "N" may be moved inside the nearby vertical stem, and we have
1537 	 * to prevent this
1538 	 */
1539 	cscale = pref_y ? genchange->hcounter_scale :
1540 	    genchange->use_vert_mapping ? genchange->v_scale : genchange->vcounter_scale;
1541 	if ( !CorrectDPointPos( gd,pd,stem,pref_y?hscale:vscale,true,is_l,pref_y ))
1542 	    ShiftDependent( gd,pd,stem,orig_b,new_b,cscale,true,is_l,pref_y );
1543 	if ( !CorrectDPointPos( gd,pd,stem,pref_y?hscale:vscale,false,is_l,pref_y ))
1544 	    ShiftDependent( gd,pd,stem,orig_b,new_b,cscale,false,is_l,pref_y );
1545 	 cscale = pref_y ? genchange->use_vert_mapping ?
1546 	     genchange->v_scale : genchange->vcounter_scale : genchange->hcounter_scale;
1547 	 ShiftDependent( gd,pd,stem,orig_b,new_b,cscale,true,is_l,!pref_y );
1548 	 ShiftDependent( gd,pd,stem,orig_b,new_b,cscale,false,is_l,!pref_y );
1549     }
1550 
1551     /* This is to fix relative positioning of starting/terminal points on a diagonal stem
1552      * which starts/finishes with a "stub" (i. e. no terminal serifs or connections with
1553      * other features), like in a slash or less/greater signs. We would expect such points
1554      * to be aligned as in the original outline, even if horizontal and vertical ratios
1555      * were different
1556      */
1557     if ( stem->keypts[0] != stem->keypts[2] && (
1558 	( stem->keypts[0]->sp->next->to == stem->keypts[2]->sp ) ||
1559 	( stem->keypts[0]->sp->prev->from == stem->keypts[2]->sp )))
1560 	AlignPointPair( stem,stem->keypts[0],stem->keypts[2],hscale,vscale );
1561 
1562     if ( stem->keypts[1] != stem->keypts[3] && (
1563 	( stem->keypts[1]->sp->next->to == stem->keypts[3]->sp ) ||
1564 	( stem->keypts[1]->sp->prev->from == stem->keypts[3]->sp )))
1565 	AlignPointPair( stem,stem->keypts[1],stem->keypts[3],hscale,vscale );
1566 }
1567 
ChangeGlyph(SplineChar * sc_sc,SplineChar * orig_sc,int layer,struct genericchange * genchange)1568 void ChangeGlyph( SplineChar *sc_sc, SplineChar *orig_sc, int layer, struct genericchange *genchange ) {
1569     real scale[6];
1570     DBounds orig_b, new_b;
1571     int i, dcnt = 0, removeoverlap = true;
1572     double owidth = orig_sc->width, ratio, add;
1573     AnchorPoint *ap;
1574     GlyphData *gd;
1575     PointData *pd;
1576     StemData **dstems = NULL;
1577     StrokeInfo si;
1578     SplineSet *temp;
1579     BlueData bd;
1580 
1581     if ( sc_sc != orig_sc ) {
1582 	sc_sc->layers[layer].splines = SplinePointListCopy( orig_sc->layers[layer].splines );
1583 	sc_sc->anchor = AnchorPointsCopy(orig_sc->anchor);
1584     }
1585     memset( &bd,0,sizeof( bd ));
1586     InitZoneMappings( &genchange->m,&bd,genchange->v_scale );
1587 
1588     if (genchange->stem_height_add!=0 && genchange->stem_width_add!=0 &&
1589 	genchange->stem_height_add/genchange->stem_width_add > 0 ) {
1590 
1591 	ratio = genchange->stem_height_add/genchange->stem_width_add;
1592 	memset( scale,0,sizeof( scale ));
1593 	if ( ratio>1 ) {
1594 	    add = genchange->stem_width_add;
1595 	    scale[0] = 1.;
1596 	    scale[3] = 1/ratio;
1597 	} else {
1598 	    add = genchange->stem_height_add;
1599 	    scale[0] = ratio;
1600 	    scale[3] = 1.;
1601 	}
1602 	SplinePointListTransform( sc_sc->layers[layer].splines,scale,tpt_AllPoints );
1603 	InitializeStrokeInfo(&si);
1604 	si.stroke_type = si_round;
1605 	SITranslatePSArgs(&si, lj_miter, lc_square);
1606 	si.rmov = srmov_contour;
1607 	if ( add >=0 ) {
1608 	    si.width = add;
1609 	    si.removeinternal = true;
1610 	} else {
1611 	    si.width = -add;
1612 	    si.removeexternal = true;
1613 	}
1614 
1615 	temp = BoldSSStroke( sc_sc->layers[layer].splines,&si,sc_sc->layers[layer].order2,removeoverlap );
1616 	SplinePointListsFree( sc_sc->layers[layer].splines );
1617 	sc_sc->layers[layer].splines = temp;
1618 	if ( ratio != 1.0 ) {
1619 	    if ( ratio>1 ) {
1620 		scale[0] = 1.;
1621 		scale[3] = ratio;
1622 	    } else {
1623 		scale[0] = 1/ratio;
1624 		scale[3] = 1.;
1625 	    }
1626 	}
1627 	SplinePointListTransform( sc_sc->layers[layer].splines,scale,tpt_AllPoints );
1628 
1629 	/* If stroke has been expanded/condensed, then the old hints are no longer */
1630 	/* relevant; so just remove them and rely solely on the stem detector */
1631 	StemInfosFree(sc_sc->hstem);  sc_sc->hstem = NULL;
1632 	StemInfosFree(sc_sc->vstem);  sc_sc->vstem = NULL;
1633 	DStemInfosFree(sc_sc->dstem); sc_sc->dstem = NULL;
1634 
1635 	/* Resize blues so that GlyphDataBuild() could snap the emboldened stems to them */
1636 	for ( i=0; i<bd.bluecnt; i++ ) {
1637 	    if ( bd.blues[i][0] < 0 ) {
1638 		bd.blues[i][0] -= genchange->stem_height_add/2;
1639 		bd.blues[i][1] -= genchange->stem_height_add/2;
1640 	    } else if ( bd.blues[i][1] > 0 ) {
1641 		bd.blues[i][1] += genchange->stem_height_add/2;
1642 		bd.blues[i][0] += genchange->stem_height_add/2;
1643 	    }
1644 	}
1645     }
1646 
1647     SplineCharLayerFindBounds( orig_sc,layer,&orig_b );
1648     memcpy( &new_b,&orig_b,sizeof( DBounds ));
1649     gd = GlyphDataBuild( sc_sc,layer,&bd,true );
1650     if ( gd == NULL )
1651 return;
1652     if (genchange->stem_height_add!=0 && genchange->stem_width_add!=0 &&
1653 	genchange->stem_height_add/genchange->stem_width_add > 0 ) {
1654 	/* Resize blues back to original values so that stems could be shifted to their original positions */
1655 	for ( i=0; i<bd.bluecnt; i++ ) {
1656 	    if ( bd.blues[i][0] < 0 ) {
1657 		bd.blues[i][0] += genchange->stem_height_add/2;
1658 		bd.blues[i][1] += genchange->stem_height_add/2;
1659 	    } else if ( bd.blues[i][1] > 0 ) {
1660 		bd.blues[i][1] -= genchange->stem_height_add/2;
1661 		bd.blues[i][0] -= genchange->stem_height_add/2;
1662 	    }
1663 	}
1664     }
1665 
1666     /* Have to prepare a DStem list before further operations, since they are needed */
1667     /* to properly calculate counters between vertical stems */
1668     if ( genchange->dstem_control ) {
1669 	dstems = calloc( gd->stemcnt,sizeof( StemData *));
1670 	dcnt = PrepareDStemList( gd,dstems );
1671     }
1672 
1673     StemResize( sc_sc->layers[layer].splines,gd,dstems,dcnt,&orig_b,&new_b,genchange,true );
1674     if ( genchange->use_vert_mapping && genchange->m.cnt > 0 )
1675 	HStemResize( sc_sc->layers[layer].splines,gd,&orig_b,&new_b,genchange );
1676     else
1677 	StemResize( sc_sc->layers[layer].splines,gd,dstems,dcnt,&orig_b,&new_b,genchange,false );
1678     PosStemPoints( gd,genchange,dcnt > 0,true );
1679     PosStemPoints( gd,genchange,dcnt > 0,false );
1680     if ( genchange->dstem_control ) {
1681 	for ( i=0; i<dcnt; i++ )
1682 	    FixDStem( gd,dstems[i],dstems,dcnt,&orig_b,&new_b,genchange );
1683     }
1684     /* Our manipulations with DStems may have moved some points outside of  */
1685     /* borders of the bounding box we have previously calculated. So adjust */
1686     /* those borders now, as they may be important for point interpolations */
1687     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
1688 	pd = &gd->points[i];
1689 	if ( pd->touched & tf_d && pd->newpos.x < new_b.minx )
1690 	    new_b.minx = pd->newpos.x;
1691 	else if ( pd->touched & tf_d && pd->newpos.x > new_b.maxx )
1692 	    new_b.maxx = pd->newpos.x;
1693 	if ( pd->touched & tf_d && pd->newpos.y < new_b.miny )
1694 	    new_b.miny = pd->newpos.y;
1695 	else if ( pd->touched & tf_d && pd->newpos.y > new_b.maxy )
1696 	    new_b.maxy = pd->newpos.y;
1697     }
1698     InterpolateStrong( gd,&orig_b,&new_b,true );
1699     InterpolateStrong( gd,&orig_b,&new_b,false );
1700     InterpolateWeak( gd,&orig_b,&new_b,genchange->stem_width_scale,true );
1701     InterpolateWeak( gd,&orig_b,&new_b,genchange->stem_height_scale,false );
1702     InterpolateAnchorPoints( gd,sc_sc->anchor,&orig_b,&new_b,genchange->hcounter_scale,true );
1703     InterpolateAnchorPoints( gd,sc_sc->anchor,&orig_b,&new_b,
1704 	genchange->use_vert_mapping ? genchange->v_scale : genchange->vcounter_scale,false );
1705 
1706     /* Finally move every point to its new location */
1707     for ( i=0; i<gd->pcnt; i++ ) if ( gd->points[i].sp != NULL ) {
1708 	pd = &gd->points[i];
1709 	pd->sp->me.x = pd->newpos.x;
1710 	pd->sp->me.y = pd->newpos.y;
1711 	if ( !pd->sp->nonextcp ) {
1712 	    pd->sp->nextcp.x = pd->newnext.x;
1713 	    pd->sp->nextcp.y = pd->newnext.y;
1714 	}
1715 	if ( !pd->sp->noprevcp ) {
1716 	    pd->sp->prevcp.x = pd->newprev.x;
1717 	    pd->sp->prevcp.y = pd->newprev.y;
1718 	}
1719     }
1720     SplineSetRefigure(sc_sc->layers[layer].splines);
1721     SplineCharLayerFindBounds(sc_sc,layer,&new_b);
1722 
1723     /* Set the left and right side bearings appropriately */
1724     memset(scale,0,sizeof(scale));
1725     scale[0] = scale[3] = 1;
1726     if ( genchange->center_in_hor_advance == 1 )
1727 	scale[4] = ( owidth - ( new_b.maxx - new_b.minx ))/2.0 - new_b.minx;
1728     else if ( genchange->center_in_hor_advance == 2 )
1729 	scale[4] = orig_b.minx/( owidth - ( orig_b.maxx - orig_b.minx )) *
1730 	    ( owidth - ( new_b.maxx - new_b.minx )) - new_b.minx;
1731     else
1732 	scale[4] = orig_b.minx*genchange->lsb_scale + genchange->lsb_add - new_b.minx;
1733     SplinePointListTransform(sc_sc->layers[layer].splines,scale,tpt_AllPoints);
1734     for ( ap = sc_sc->anchor; ap!=NULL; ap=ap->next )
1735 	ap->me.x += scale[4];
1736     SplineCharLayerFindBounds( sc_sc,layer,&new_b );
1737     if ( !genchange->center_in_hor_advance )
1738 	sc_sc->width = ( owidth-orig_b.maxx )*genchange->rsb_scale + genchange->rsb_add + new_b.maxx;
1739 
1740     /* If it is a subscript/superscript glyph, then move it to the desired vertical position */
1741     memset(scale,0,sizeof(scale));
1742     scale[0] = scale[3] = 1;
1743     scale[5] = genchange->vertical_offset;
1744     SplinePointListTransform(sc_sc->layers[layer].splines,scale,tpt_AllPoints);
1745     for ( ap = sc_sc->anchor; ap!=NULL; ap=ap->next )
1746 	ap->me.y += genchange->vertical_offset;
1747 
1748     if ( genchange->dstem_control )
1749 	free( dstems );
1750     GlyphDataFree( gd );
1751     StemInfosFree( sc_sc->hstem );  sc_sc->hstem = NULL;
1752     StemInfosFree( sc_sc->vstem );  sc_sc->vstem = NULL;
1753     DStemInfosFree( sc_sc->dstem ); sc_sc->dstem = NULL;
1754     SCRound2Int( sc_sc,layer, 1.0 );		/* This calls SCCharChangedUpdate(sc_sc,layer); */
1755 }
1756 
1757 /* ************************************************************************** */
1758 /* ***************************** Small Capitals ***************************** */
1759 /* ************************************************************************** */
1760 
1761 
1762 extern int autohint_before_generate;
1763 
NumberLayerPoints(SplineSet * ss)1764 static int NumberLayerPoints(SplineSet *ss) {
1765     int cnt;
1766     SplinePoint *pt;
1767 
1768     cnt = 1;
1769     for ( ; ss!=NULL; ss=ss->next ) {
1770 	for ( pt=ss->first; ; ) {
1771 	    pt->ptindex = cnt++;
1772 	    if ( pt->next==NULL )
1773 	break;
1774 	    pt = pt->next->to;
1775 	    if ( pt==ss->first )
1776 	break;
1777 	}
1778     }
1779 return( cnt );
1780 }
1781 
CaseMajorVerticalStemWidth(SplineFont * sf,int layer,unichar_t * list,double tan_ia)1782 static double CaseMajorVerticalStemWidth(SplineFont *sf, int layer,
1783 	unichar_t *list, double tan_ia) {
1784     const int MW=100;
1785     struct widths { double width, total; } widths[MW];
1786     int cnt,i,j;
1787     double width, sum, total;
1788     SplineChar *sc, dummy, *which;
1789     Layer layers[2];
1790     char *snaps, *end;
1791     StemInfo *s;
1792     double val, diff, bestwidth, bestdiff;
1793     real deskew[6];
1794 
1795     memset(deskew,0,sizeof(deskew));
1796     deskew[0] = deskew[3] = 1;
1797     deskew[2] = tan_ia;
1798 
1799     cnt = 0;
1800     for ( i=0; list[i]!=0; ++i ) {
1801 	sc = SFGetChar(sf,list[i],NULL);
1802 	if ( sc==NULL )
1803     continue;
1804 	if ( tan_ia== 0 && autohint_before_generate && (sc->changedsincelasthinted || sc->vstem==NULL ) &&
1805 		!sc->manualhints )
1806 	    SplineCharAutoHint(sc,layer,NULL);
1807 	if ( tan_ia==0 && sc->vstem!=NULL ) {
1808 	    which = sc;
1809 	} else {
1810 /* Ok, we couldn't hint it, or it was italic. Make a copy of it, deskew it, */
1811 /*  and hint that */
1812 	    memset(&dummy,0,sizeof(dummy));
1813 	    memset(layers,0,sizeof(layers));
1814 	    dummy.color = COLOR_DEFAULT;
1815 	    dummy.layer_cnt = 2;
1816 	    dummy.layers = layers;
1817 	    dummy.parent = sc->parent;
1818 	    dummy.name = copy("Fake");
1819 
1820 	    dummy.layers[ly_fore].order2 = sc->layers[layer].order2;
1821 	    dummy.layers[ly_fore].splines = SplinePointListTransform(
1822 		    SplinePointListCopy(LayerAllSplines(&sc->layers[layer])),
1823 		    deskew,tpt_AllPoints);
1824 	    LayerUnAllSplines(&sc->layers[ly_fore]);
1825 
1826 	    SplineCharAutoHint(&dummy,ly_fore,NULL);
1827 	    which = &dummy;
1828 	}
1829 	for ( s= which->vstem; s!=NULL; s=s->next ) if ( !s->ghost ) {
1830 	    for ( j=0; j<cnt; ++j )
1831 		if ( widths[j].width==s->width )
1832 	    break;
1833 	    if ( j<cnt )
1834 		widths[j].total += HIlen(s);
1835 	    else if ( j<MW ) {
1836 		++cnt;
1837 		widths[j].width = s->width;
1838 		widths[j].total = HIlen(s);
1839 	    }
1840 	}
1841 	if ( which==&dummy )
1842 	    SCClearContents(&dummy,ly_fore);
1843     }
1844     if ( cnt==0 )
1845 return( -1 );
1846 
1847     /* Is there a width that occurs significantly more often than any other? */
1848     for ( i=0; i<cnt; ++i ) for ( j=i+1; j<cnt; ++j ) {
1849 	if ( widths[i].total > widths[j].total ) {
1850 	    struct widths temp;
1851 	    temp = widths[j];
1852 	    widths[j] = widths[i];
1853 	    widths[i] = temp;
1854 	}
1855     }
1856     if ( cnt==1 || widths[cnt-1].total > 1.5*widths[cnt-2].total )
1857 	width = widths[cnt-1].width;
1858     else {
1859 	/* Ok, that didn't work find an average */
1860 	sum = 0; total=0;
1861 	for ( i=0; i<cnt; ++i ) {
1862 	    sum += widths[i].total*widths[i].width;
1863 	    total += widths[i].total;
1864 	}
1865 	width = sum/total;
1866     }
1867 
1868     /* Do we have a StemSnapV entry? */
1869     /* If so, snap width to the closest value in it */
1870     if ( sf->private!=NULL && (snaps = PSDictHasEntry(sf->private,"StemSnapV"))!=NULL ) {
1871 	while ( *snaps==' ' || *snaps=='[' ) ++snaps;
1872 	/* Must get at least this close, else we'll just use what we found */
1873 	bestwidth = width; bestdiff = (sf->ascent+sf->descent)/100.0;
1874 	while ( *snaps!='\0' && *snaps!=']' ) {
1875 	    val = strtod(snaps,&end);
1876 	    if ( snaps==end )
1877 	break;
1878 	    snaps = end;
1879 	    while ( *snaps==' ' ) ++snaps;
1880 	    if ( (diff = val-width)<0 ) diff = -diff;
1881 	    if ( diff<bestdiff ) {
1882 		bestwidth = val;
1883 		bestdiff = diff;
1884 	    }
1885 	}
1886 	width = bestwidth;
1887     }
1888 return( width );
1889 }
1890 
SmallCapsFindConstants(struct smallcaps * small,SplineFont * sf,int layer)1891 void SmallCapsFindConstants(struct smallcaps *small, SplineFont *sf,
1892 	int layer ) {
1893 
1894     memset(small,0,sizeof(*small));
1895 
1896     small->sf = sf; small->layer = layer;
1897     small->italic_angle = sf->italicangle * FF_PI/180.0;
1898     small->tan_ia = tan( small->italic_angle );
1899 
1900     small->lc_stem_width = CaseMajorVerticalStemWidth(sf, layer,lc_stem_str, small->tan_ia );
1901     small->uc_stem_width = CaseMajorVerticalStemWidth(sf, layer,uc_stem_str, small->tan_ia );
1902 
1903     if ( small->uc_stem_width<=small->lc_stem_width || small->lc_stem_width==0 )
1904 	small->stem_factor = 1;
1905     else
1906 	small->stem_factor = small->lc_stem_width / small->uc_stem_width;
1907     small->v_stem_factor = small->stem_factor;
1908 
1909     small->xheight   = SFXHeight  (sf,layer,false);
1910     small->capheight = SFCapHeight(sf,layer,false);
1911     small->scheight  = small->xheight;
1912     if ( small->capheight>0 )
1913 	small->vscale = small->scheight / small->capheight;
1914     else
1915 	small->vscale = .75;
1916     small->hscale     = small->vscale;
1917 }
1918 
MakeLookups(SplineFont * sf,OTLookup ** lookups,int ltn,int crl,int grk,int symbols,uint32 ftag)1919 static void MakeLookups(SplineFont *sf,OTLookup **lookups,int ltn,int crl,int grk,
1920 	int symbols, uint32 ftag) {
1921     OTLookup *any = NULL;
1922     int i;
1923     struct lookup_subtable *sub;
1924 
1925     for ( i=0; i<3; ++i ) {
1926 	if ( any==NULL )
1927 	    any = lookups[i];
1928 	else if ( lookups[i]!=NULL && lookups[i]!=any )
1929 	    any = (OTLookup *) -1;
1930     }
1931 
1932     if ( any==(OTLookup *) -1 ) {
1933 	/* Each script has it's own lookup. So if we are missing a script we */
1934 	/*  should create a new lookup for it */
1935 	if ( lookups[0]==NULL && ltn ) {
1936 	    sub = SFSubTableFindOrMake(sf,ftag,CHR('l','a','t','n'),gsub_single);
1937 	    lookups[0] = sub->lookup;
1938 	}
1939 	if ( lookups[1]==NULL && crl ) {
1940 	    sub = SFSubTableFindOrMake(sf,ftag,CHR('c','y','r','l'),gsub_single);
1941 	    lookups[1] = sub->lookup;
1942 	}
1943 	if ( lookups[2]==NULL && grk ) {
1944 	    sub = SFSubTableFindOrMake(sf,ftag,CHR('g','r','e','k'),gsub_single);
1945 	    lookups[2] = sub->lookup;
1946 	}
1947 	if ( lookups[3]==NULL && symbols ) {
1948 	    sub = SFSubTableFindOrMake(sf,ftag,DEFAULT_SCRIPT,gsub_single);
1949 	    lookups[3] = sub->lookup;
1950 	}
1951     } else {
1952 	if ( any!=NULL ) {
1953 	    /* There's only one lookup, let's extend it to deal with any script */
1954 	    /*  we need for which there is no lookup */
1955 	} else {
1956 	    /* No lookup. Create one for all the scripts we need */
1957 	    sub = SFSubTableFindOrMake(sf,ftag,
1958 		    ltn?CHR('l','a','t','n'):
1959 		    crl?CHR('c','y','r','l'):
1960 			CHR('g','r','e','k'),gsub_single);
1961 	    any = sub->lookup;
1962 	}
1963 	if ( lookups[0]==NULL && ltn ) {
1964 	    lookups[0] = any;
1965 	    FListAppendScriptLang(FindFeatureTagInFeatureScriptList(ftag,any->features),CHR('l','a','t','n'),DEFAULT_LANG);
1966 	}
1967 	if ( lookups[1]==NULL && crl ) {
1968 	    lookups[1] = any;
1969 	    FListAppendScriptLang(FindFeatureTagInFeatureScriptList(ftag,any->features),CHR('c','y','r','l'),DEFAULT_LANG);
1970 	}
1971 	if ( lookups[2]==NULL && grk ) {
1972 	    lookups[2] = any;
1973 	    FListAppendScriptLang(FindFeatureTagInFeatureScriptList(ftag,any->features),CHR('g','r','e','k'),DEFAULT_LANG);
1974 	}
1975 	if ( lookups[3]==NULL && symbols ) {
1976 	    lookups[3] = any;
1977 	    FListAppendScriptLang(FindFeatureTagInFeatureScriptList(ftag,any->features),DEFAULT_SCRIPT,DEFAULT_LANG);
1978 	}
1979     }
1980     for ( i=0; i<4; ++i ) {
1981 	if ( lookups[i]!=NULL && lookups[i]->subtables==NULL ) {
1982 	    lookups[i]->subtables = chunkalloc(sizeof(struct lookup_subtable));
1983 	    lookups[i]->subtables->lookup = lookups[i];
1984 	    lookups[i]->subtables->per_glyph_pst_or_kern = true;
1985 	    NameOTLookup(lookups[i],sf);
1986 	}
1987     }
1988 }
1989 
MakeSCLookups(SplineFont * sf,struct lookup_subtable ** c2sc,struct lookup_subtable ** smcp,int ltn,int crl,int grk,int symbols,int petite)1990 static void MakeSCLookups(SplineFont *sf,struct lookup_subtable **c2sc,
1991 	struct lookup_subtable **smcp,
1992 	int ltn,int crl,int grk,int symbols,int petite ) {
1993     OTLookup *test;
1994     FeatureScriptLangList *fl;
1995     struct scriptlanglist *sl;
1996     OTLookup *lc2sc[4], *lsmcp[4];
1997     int i;
1998     uint32 ucfeat, lcfeat;
1999 
2000     memset(lc2sc,0,sizeof(lc2sc)); memset(lsmcp,0,sizeof(lsmcp));
2001 
2002     if ( petite ) {
2003 	ucfeat = CHR('c','2','p','c');
2004 	lcfeat = CHR('p','c','a','p');
2005     } else {
2006 	ucfeat = CHR('c','2','s','c');
2007 	lcfeat = CHR('s','m','c','p');
2008     }
2009     if ( sf->cidmaster ) sf=sf->cidmaster;
2010     for ( test=sf->gsub_lookups; test!=NULL; test=test->next ) if ( test->lookup_type==gsub_single ) {
2011 	for ( fl=test->features; fl!=NULL; fl=fl->next ) {
2012 	    if ( fl->featuretag==ucfeat || fl->featuretag==lcfeat ) {
2013 		for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
2014 		    if ( sl->script==CHR('l','a','t','n')) {
2015 			if ( fl->featuretag==ucfeat )
2016 			    lc2sc[0] = test;
2017 			else
2018 			    lsmcp[0] = test;
2019 		    } else if ( sl->script==CHR('c','y','r','l')) {
2020 			if ( fl->featuretag==ucfeat )
2021 			    lc2sc[1] = test;
2022 			else
2023 			    lsmcp[1] = test;
2024 		    } else if ( sl->script==CHR('g','r','e','k')) {
2025 			if ( fl->featuretag==ucfeat )
2026 			    lc2sc[2] = test;
2027 			else
2028 			    lsmcp[2] = test;
2029 		    }
2030 		}
2031 	    }
2032 	}
2033     }
2034 
2035     MakeLookups(sf,lc2sc,ltn,crl,grk,symbols,ucfeat);
2036     MakeLookups(sf,lsmcp,ltn,crl,grk,symbols,lcfeat);
2037 
2038     for ( i=0; i<4; ++i ) {
2039 	if ( lc2sc[i]!=NULL )
2040 	    c2sc[i] = lc2sc[i]->subtables;
2041 	if ( lsmcp[i]!=NULL )
2042 	    smcp[i] = lsmcp[i]->subtables;
2043     }
2044 }
2045 
MakeSmallCapName(char * buffer,int bufsize,SplineFont * sf,SplineChar * sc,struct genericchange * genchange)2046 static SplineChar *MakeSmallCapName(char *buffer, int bufsize, SplineFont *sf,
2047 	SplineChar *sc,struct genericchange *genchange) {
2048     SplineChar *lc_sc;
2049     const char *ext;
2050     int lower;
2051 
2052     if ( sc->unicodeenc>=0 && sc->unicodeenc<0x10000 ) {
2053 	lower = tolower(sc->unicodeenc);
2054 	ext = isupper(sc->unicodeenc) ? genchange->extension_for_letters :
2055 	      islower(sc->unicodeenc) ? genchange->extension_for_letters :
2056 		sc->unicodeenc==0xdf  ? genchange->extension_for_letters :
2057 		sc->unicodeenc>=0xfb00 && sc->unicodeenc<=0xfb06 ? genchange->extension_for_letters :
2058 					    genchange->extension_for_symbols;
2059     } else {
2060 	lower = sc->unicodeenc;
2061 	ext = genchange->extension_for_symbols;
2062     }
2063     lc_sc = SFGetChar(sf,lower,NULL);
2064     if ( lc_sc!=NULL )
2065 	snprintf(buffer,bufsize,"%s.%s", lc_sc->name, ext );
2066     else {
2067 	const char *pt = StdGlyphName(buffer,lower,sf->uni_interp,sf->for_new_glyphs);
2068 	if ( pt!=buffer )
2069 	    strcpy(buffer,pt);
2070 	strcat(buffer,".");
2071 	strcat(buffer,ext);
2072     }
2073 return( lc_sc );
2074 }
2075 
MakeSmallCapGlyphSlot(SplineFont * sf,SplineChar * cap_sc,uint32 script,struct lookup_subtable ** c2sc,struct lookup_subtable ** smcp,FontViewBase * fv,struct genericchange * genchange)2076 static SplineChar *MakeSmallCapGlyphSlot(SplineFont *sf,SplineChar *cap_sc,
2077 	uint32 script,struct lookup_subtable **c2sc,struct lookup_subtable **smcp,
2078 	FontViewBase *fv, struct genericchange *genchange) {
2079     SplineChar *sc_sc, *lc_sc;
2080     char buffer[300];
2081     PST *pst;
2082     int enc;
2083     int script_index;
2084 
2085     lc_sc = MakeSmallCapName(buffer,sizeof(buffer),sf,cap_sc,genchange);
2086     sc_sc = SFGetChar(sf,-1,buffer);
2087     if ( sc_sc!=NULL ) {
2088 	SCPreserveLayer(sc_sc,fv->active_layer,false);
2089 	SCClearLayer(sc_sc,fv->active_layer);
2090 return( sc_sc );
2091     }
2092     enc = SFFindSlot(sf, fv->map, -1, buffer );
2093     if ( enc==-1 )
2094 	enc = fv->map->enccount;
2095     sc_sc = SFMakeChar(sf,fv->map,enc);
2096     free(sc_sc->name);
2097     sc_sc->name = copy(buffer);
2098     SFHashGlyph(sf,sc_sc);
2099 
2100     pst = chunkalloc(sizeof(PST));
2101     pst->next = cap_sc->possub;
2102     cap_sc->possub = pst;
2103     script_index = script==CHR('l','a','t','n')?0:
2104 			 script==CHR('c','y','r','l')?1:
2105 			 script==CHR('g','r','e','k')?2:3;
2106     pst->subtable = c2sc[script_index];
2107     pst->type = pst_substitution;
2108     pst->u.subs.variant = copy(buffer);
2109 
2110     /* Adobe's convention seems to be that symbols get both the lc and the uc */
2111     /*  feature attached to them. */
2112     if ( lc_sc!=NULL ) {
2113 	pst = chunkalloc(sizeof(PST));
2114 	pst->next = lc_sc->possub;
2115 	lc_sc->possub = pst;
2116 	pst->subtable = smcp[script_index];
2117 	pst->type = pst_substitution;
2118 	pst->u.subs.variant = copy(buffer);
2119     }
2120 return( sc_sc );
2121 }
2122 
2123 struct overlaps { double start, stop, new_start, new_stop; };
2124 
SCFindHintOverlaps(StemInfo * hints,double min_coord,double max_coord,int * _tot,double * _counter_len)2125 static struct overlaps *SCFindHintOverlaps(StemInfo *hints,double min_coord,
2126 	double max_coord, int *_tot, double *_counter_len) {
2127     struct overlaps *overlaps;
2128     StemInfo *h;
2129     int cnt, tot, i, j;
2130     double counter_len;
2131 
2132     for ( h=hints, cnt=0; h!=NULL; h=h->next ) if ( !h->ghost )
2133 	++cnt;
2134 
2135     overlaps = malloc((cnt+3)*sizeof(struct overlaps));
2136     overlaps[0].start = min_coord; overlaps[0].stop = min_coord;
2137     overlaps[1].start = max_coord; overlaps[1].stop = max_coord;
2138     tot = 2;
2139 
2140     for ( h=hints; h!=NULL; h=h->next ) if ( !h->ghost ) {
2141 	for ( i=0; i<tot && overlaps[i].stop<h->start; ++i );
2142 	if ( i==tot )	/* Can't happen */
2143     continue;
2144 	/* So h->start<=overlaps[i].stop */
2145 	if ( h->start+h->width<overlaps[i].start ) {
2146 	    /* New entry */
2147 	    for ( j=tot; j>i; --j )
2148 		overlaps[j] = overlaps[j-1];
2149 	    overlaps[i].start = h->start;
2150 	    overlaps[i].stop  = h->start+h->width;
2151 	    ++tot;
2152 	} else {
2153 	    if ( h->start<overlaps[i].start )
2154 		overlaps[i].start = h->start;
2155 	    if ( h->start+h->width > overlaps[i].stop )
2156 		overlaps[i].stop = h->start+h->width;
2157 	    while ( i+1<tot && overlaps[i].stop>=overlaps[i+1].start ) {
2158 		overlaps[i].stop = overlaps[i+1].stop;
2159 		--tot;
2160 		for ( j=i+1; j<tot; ++j )
2161 		    overlaps[j] = overlaps[j+1];
2162 	    }
2163 	}
2164     }
2165     for ( i=0, counter_len=0; i<tot-1; ++i )
2166 	counter_len += overlaps[i+1].start-overlaps[i].stop;
2167 
2168     *_tot = tot;
2169     *_counter_len = counter_len;
2170 return( overlaps );
2171 }
2172 
SCFindCounterLen(StemInfo * hints,double min_coord,double max_coord)2173 static double SCFindCounterLen(StemInfo *hints,double min_coord,
2174 	double max_coord) {
2175     int tot=0;
2176     double counter_len=0;
2177     struct overlaps *overlaps;
2178 
2179     overlaps = SCFindHintOverlaps(hints,min_coord,max_coord,&tot,&counter_len);
2180     free(overlaps);
2181 return( counter_len );
2182 }
2183 
SmallCapsPlacePoints(SplineSet * ss,AnchorPoint * aps,int coord,StemInfo * hints,struct overlaps * overlaps,int tot)2184 static void SmallCapsPlacePoints(SplineSet *ss,AnchorPoint *aps,
2185 	int coord, StemInfo *hints,
2186 	struct overlaps *overlaps, int tot) {
2187     struct ptpos { double old, new; int hint_index; } *ptpos;
2188     int cnt, i, order2;
2189     double val;
2190     StemInfo *h;
2191     SplineSet *spl;
2192     SplinePoint *sp, *first, *start, *last;
2193     AnchorPoint *ap;
2194 
2195     cnt = NumberLayerPoints(ss);
2196     ptpos = calloc(cnt,sizeof(struct ptpos));
2197 
2198     /* Position any points which lie within a hint zone */
2199     order2 = false;
2200     for ( spl=ss; spl!=NULL; spl=spl->next ) {
2201 	for ( sp=spl->first; ; ) {
2202 	    sp->ticked = false;
2203 	    val = (&sp->me.x)[coord];
2204 	    ptpos[sp->ptindex].old = val;
2205 	    ptpos[sp->ptindex].hint_index = -2;
2206 	    for ( i=0; i<tot; ++i ) {
2207 		if ( val>=overlaps[i].start && val<=overlaps[i].stop ) {
2208 		    for ( h=hints; h!=NULL; h=h->next ) {
2209 			if ( RealNear(val,h->start) || RealNear(val,h->start+h->width)) {
2210 			    sp->ticked = true;
2211 			    ptpos[sp->ptindex].hint_index = i;
2212 			    ptpos[sp->ptindex].new = overlaps[i].new_start +
2213 				    (val-overlaps[i].start);
2214 		    break;
2215 			}
2216 		    }
2217 	    break;
2218 		}
2219 	    }
2220 	    if ( sp->next==NULL )
2221 	break;
2222 	    order2 = sp->next->order2;
2223 	    sp = sp->next->to;
2224 	    if ( sp==spl->first )
2225 	break;
2226 	}
2227     }
2228 
2229     /* Look for any local minimum or maximum points */
2230     for ( spl=ss; spl!=NULL; spl=spl->next ) {
2231 	for ( sp=spl->first; ; ) {
2232 	    if ( sp->next==NULL )
2233 	break;
2234 	    val = (&sp->me.x)[coord];
2235 	    if ( !sp->ticked && ( IsExtremum(sp,coord) || IsAnglePoint(sp))) {
2236 		for ( i=0; i<tot; ++i ) {
2237 		    if ( val>=overlaps[i].start && val<overlaps[i].stop ) {
2238 			sp->ticked = true;
2239 			ptpos[sp->ptindex].new = overlaps[i].new_start +
2240 				(val-overlaps[i].start) *
2241 				    (overlaps[i].new_stop - overlaps[i].new_start)/
2242 				    (overlaps[i].stop - overlaps[i].start);
2243 		break;
2244 		    } else if ( i>0 && val>=overlaps[i-1].stop && val<=overlaps[i].start ) {
2245 			sp->ticked = true;
2246 			ptpos[sp->ptindex].new = overlaps[i-1].new_stop +
2247 				(val-overlaps[i-1].stop) *
2248 				    (overlaps[i].new_start - overlaps[i-1].new_stop)/
2249 				    (overlaps[i].start - overlaps[i-1].stop);
2250 		break;
2251 		    }
2252 		}
2253 	    }
2254 	    sp = sp->next->to;
2255 	    if ( sp==spl->first )
2256 	break;
2257 	}
2258     }
2259 
2260     /* Position any points which line between points already positioned */
2261     for ( spl=ss; spl!=NULL; spl=spl->next ) {
2262 	for ( sp=spl->first; !sp->ticked; ) {
2263 	    if ( sp->next==NULL )
2264 	break;
2265 	    sp = sp->next->to;
2266 	    if ( sp==spl->first )
2267 	break;
2268 	}
2269 	if ( !sp->ticked )		/* Nothing on this contour positioned */
2270     continue;
2271 	first = sp;
2272 	do {
2273 	    start = sp;
2274 	    if ( sp->next==NULL )
2275 	break;
2276 	    /* Find the next point which has been positioned */
2277 	    for ( sp=sp->next->to; !sp->ticked; ) {
2278 		if ( sp->next==NULL )
2279 	    break;
2280 		sp = sp->next->to;
2281 		if ( sp==first )
2282 	    break;
2283 	    }
2284 	    if ( !sp->ticked )
2285 	break;
2286 	    /* Interpolate any points between these two */
2287 	    last = sp;
2288 	    /* Make sure all the points are BETWEEN */
2289 	    for ( sp=start->next->to; sp!=last; sp=sp->next->to ) {
2290 		if (( (&sp->me.x)[coord] < (&start->me.x)[coord] && (&sp->me.x)[coord] < (&last->me.x)[coord]) ||
2291 			( (&sp->me.x)[coord] > (&start->me.x)[coord] && (&sp->me.x)[coord] > (&last->me.x)[coord]))
2292 	    break;
2293 	    }
2294 	    if ( sp==last ) {
2295 		for ( sp=start->next->to; sp!=last; sp=sp->next->to ) {
2296 		    ptpos[sp->ptindex].new = ptpos[start->ptindex].new +
2297 			    ((&sp->me.x)[coord] - ptpos[start->ptindex].old) *
2298 			      (ptpos[last->ptindex].new - ptpos[start->ptindex].new) /
2299 			      (ptpos[last->ptindex].old - ptpos[start->ptindex].old);
2300 		    sp->ticked = true;
2301 		}
2302 	    } else
2303 		sp = last;
2304 	} while ( sp!=first );
2305     }
2306 
2307     /* Any points which aren't currently positioned, just interpolate them */
2308     /*  between the hint zones between which they lie */
2309     /* I don't think this can actually happen... but do it just in case */
2310     for ( spl=ss; spl!=NULL; spl=spl->next ) {
2311 	for ( sp=spl->first; ; ) {
2312 	    if ( !sp->ticked ) {
2313 		val = (&sp->me.x)[coord];
2314 		for ( i=0; i<tot; ++i ) {
2315 		    if ( val>=overlaps[i].start && val<overlaps[i].stop ) {
2316 			sp->ticked = true;
2317 			ptpos[sp->ptindex].new = overlaps[i].new_start +
2318 				(val-overlaps[i].start) *
2319 				    (overlaps[i].new_stop - overlaps[i].new_start)/
2320 				    (overlaps[i].stop - overlaps[i].start);
2321 		break;
2322 		    } else if ( i>0 && val>=overlaps[i-1].stop && val<=overlaps[i].start ) {
2323 			sp->ticked = true;
2324 			ptpos[sp->ptindex].new = overlaps[i-1].new_stop +
2325 				(val-overlaps[i-1].stop) *
2326 				    (overlaps[i].new_start - overlaps[i-1].new_stop)/
2327 				    (overlaps[i].start - overlaps[i-1].stop);
2328 		break;
2329 		    }
2330 		}
2331 		if ( !sp->ticked ) {
2332 		    IError( "Unticked point in remove space (smallcaps/italic/etc.)" );
2333 		    ptpos[sp->ptindex].new = ptpos[sp->ptindex].old;
2334 		}
2335 	    }
2336 	    if ( sp->next==NULL )
2337 	break;
2338 	    sp = sp->next->to;
2339 	    if ( sp==spl->first )
2340 	break;
2341 	}
2342     }
2343     /* And do the same for anchor points */
2344     for ( ap=aps; ap!=NULL; ap=ap->next ) {
2345 	val = (&ap->me.x)[coord];
2346 	for ( i=0; i<tot; ++i ) {
2347 	    if ( val>=overlaps[i].start && val<overlaps[i].stop ) {
2348 		(&ap->me.x)[coord] = overlaps[i].new_start +
2349 			(val-overlaps[i].start) *
2350 			    (overlaps[i].new_stop - overlaps[i].new_start)/
2351 			    (overlaps[i].stop - overlaps[i].start);
2352 	break;
2353 	    } else if ( i>0 && val>=overlaps[i-1].stop && val<=overlaps[i].start ) {
2354 		(&ap->me.x)[coord] = overlaps[i-1].new_stop +
2355 			(val-overlaps[i-1].stop) *
2356 			    (overlaps[i].new_start - overlaps[i-1].new_stop)/
2357 			    (overlaps[i].start - overlaps[i-1].stop);
2358 	break;
2359 	    }
2360 	}
2361 	/* Anchor points might be outside the bounding box */
2362 	if ( i==tot ) {
2363 	    if ( val<overlaps[0].start )
2364 		(&ap->me.x)[coord] += overlaps[0].new_start - overlaps[0].start;
2365 	    else
2366 		(&ap->me.x)[coord] += overlaps[tot-1].new_stop - overlaps[tot-1].stop;
2367 	}
2368     }
2369 
2370     /* Interpolate the control points. More complex in order2. We want to */
2371     /*  preserve interpolated points, but simplified as we only have one cp */
2372     if ( !order2 ) {
2373 	for ( spl=ss; spl!=NULL; spl=spl->next ) {
2374 	    for ( sp=spl->first; ; ) {
2375 		if ( sp->prev!=NULL ) {
2376 		    if ( ptpos[sp->prev->from->ptindex].old == ptpos[sp->ptindex].old )
2377 			(&sp->prevcp.x)[coord] = ptpos[sp->ptindex].new;
2378 		    else
2379 			(&sp->prevcp.x)[coord] = ptpos[sp->ptindex].new +
2380 				((&sp->prevcp.x)[coord] - ptpos[sp->ptindex].old)*
2381 				(ptpos[sp->prev->from->ptindex].new-ptpos[sp->ptindex].new)/
2382 				(ptpos[sp->prev->from->ptindex].old-ptpos[sp->ptindex].old);
2383 		}
2384 		if ( sp->next==NULL )
2385 	    break;
2386 		if ( ptpos[sp->next->to->ptindex].old == ptpos[sp->ptindex].old )
2387 		    (&sp->nextcp.x)[coord] = ptpos[sp->ptindex].new;
2388 		else
2389 		    (&sp->nextcp.x)[coord] = ptpos[sp->ptindex].new +
2390 			    ((&sp->nextcp.x)[coord] - ptpos[sp->ptindex].old)*
2391 			    (ptpos[sp->next->to->ptindex].new-ptpos[sp->ptindex].new)/
2392 			    (ptpos[sp->next->to->ptindex].old-ptpos[sp->ptindex].old);
2393 		sp = sp->next->to;
2394 		if ( sp==spl->first )
2395 	    break;
2396 	    }
2397 	}
2398     } else {
2399 	for ( spl=ss; spl!=NULL; spl=spl->next ) {
2400 	    for ( sp=spl->first; ; ) {
2401 		sp->ticked = SPInterpolate(sp);
2402 		if ( sp->next==NULL )
2403 	    break;
2404 		if ( ptpos[sp->next->to->ptindex].old == ptpos[sp->ptindex].old )
2405 		    (&sp->nextcp.x)[coord] = ptpos[sp->ptindex].new;
2406 		else
2407 		    (&sp->nextcp.x)[coord] = ptpos[sp->ptindex].new +
2408 			    ((&sp->nextcp.x)[coord] - ptpos[sp->ptindex].old)*
2409 			    (ptpos[sp->next->to->ptindex].new-ptpos[sp->ptindex].new)/
2410 			    (ptpos[sp->next->to->ptindex].old-ptpos[sp->ptindex].old);
2411 		(&sp->next->to->prevcp.x)[coord] = (&sp->nextcp.x)[coord];
2412 		sp = sp->next->to;
2413 		if ( sp==spl->first )
2414 	    break;
2415 	    }
2416 	    for ( sp=spl->first; ; ) {
2417 		if ( sp->ticked ) {
2418 		    ptpos[sp->ptindex].new = ((&sp->nextcp.x)[coord] + (&sp->prevcp.x)[coord])/2;
2419 		}
2420 		if ( sp->next==NULL )
2421 	    break;
2422 		sp = sp->next->to;
2423 		if ( sp==spl->first )
2424 	    break;
2425 	    }
2426 	}
2427     }
2428 
2429     /* Finally move every point to its new location */
2430     for ( spl=ss; spl!=NULL; spl=spl->next ) {
2431 	for ( sp=spl->first; ; ) {
2432 	    (&sp->me.x)[coord] = ptpos[sp->ptindex].new;
2433 	    if ( sp->next==NULL )
2434 	break;
2435 	    sp = sp->next->to;
2436 	    if ( sp==spl->first )
2437 	break;
2438 	}
2439     }
2440 
2441     free(ptpos);
2442 }
2443 
SmallCapsRemoveSpace(SplineSet * ss,AnchorPoint * aps,StemInfo * hints,int coord,double remove,double min_coord,double max_coord)2444 static double SmallCapsRemoveSpace(SplineSet *ss,AnchorPoint *aps,StemInfo *hints,int coord,double remove,
2445 	double min_coord, double max_coord ) {
2446     struct overlaps *overlaps;
2447     int i, tot, set;
2448     double counter_len, shrink;
2449 
2450     if ( remove > max_coord-min_coord )
2451 return(0);
2452 
2453     /* Coalesce overlapping hint zones. These won't shrink, but the counters */
2454     /*  between them will */
2455     overlaps = SCFindHintOverlaps(hints, min_coord, max_coord, &tot, &counter_len );
2456     /* A glyph need not have any counters at all (lower case "l" doesn't) */
2457     if ( counter_len==0 ) {
2458 	free( overlaps );
2459 return( 0 );
2460     }
2461 
2462     if ( 1.5*remove > counter_len ) {
2463 	/* The amount we need to remove is disproportionate to the counter */
2464 	/*  space we have available from which to remove it. So just remove */
2465 	/*  what seems reasonable */
2466 	remove = 2*counter_len/3;
2467     }
2468 
2469     shrink = (counter_len-remove)/counter_len;
2470     /* 0 is a fixed point */
2471     /* extra->current is a known point */
2472     for ( i=0; i<tot && overlaps[i].stop<0; ++i );
2473     if ( i==tot ) {
2474 	/* glyph is entirely <0 */
2475 	set = tot-1;
2476 	overlaps[set].new_stop = shrink*overlaps[set].stop;
2477 	overlaps[set].new_start = overlaps[set].new_stop - (overlaps[set].stop - overlaps[set].start);
2478     } else if ( overlaps[i].start>0 ) {
2479 	set = i;
2480 	overlaps[set].new_start = shrink*overlaps[set].start;
2481 	overlaps[set].new_stop  = overlaps[set].new_start + (overlaps[set].stop - overlaps[set].start);
2482     } else {
2483 	set = i;
2484 	overlaps[set].new_start = overlaps[set].start;
2485 	overlaps[set].new_stop = overlaps[set].stop;
2486     }
2487     for ( i=set+1; i<tot; ++i ) {
2488 	overlaps[i].new_start = overlaps[i-1].new_stop +
2489 		(overlaps[i].start - overlaps[i-1].stop)*shrink;
2490 	overlaps[i].new_stop  = overlaps[i].new_start +
2491 		(overlaps[i].stop  -overlaps[i].start);
2492     }
2493     for ( i=set-1; i>=0; --i ) {
2494 	overlaps[i].new_stop = overlaps[i+1].new_start -
2495 		(overlaps[i+1].start - overlaps[i].stop)*shrink;
2496 	overlaps[i].new_start = overlaps[i].new_stop - (overlaps[i].stop - overlaps[i].start);
2497     }
2498 
2499     SmallCapsPlacePoints(ss,aps,coord,hints,overlaps, tot);
2500     free(overlaps);
2501 return( remove );
2502 }
2503 
LowerCaseRemoveSpace(SplineSet * ss,AnchorPoint * aps,StemInfo * hints,int coord,struct fixed_maps * fix)2504 static void LowerCaseRemoveSpace(SplineSet *ss,AnchorPoint *aps,StemInfo *hints,int coord,
2505 	struct fixed_maps *fix ) {
2506     struct overlaps *overlaps;
2507     int i,j,k,l, tot;
2508     double counter_len, shrink;
2509 
2510     /* This is a variant on the previous routine. Instead of removing a given */
2511     /*  amount to be spread out among all the counters, here we are given */
2512     /*  certain locations and told where they map to. Basically the same idea */
2513     /*  except the glyph is sub-divided and each chunk can have a different */
2514     /*  amount/proportion removed from it */
2515     /* The end maps will be for the min/max points of the glyph. so they will */
2516     /*  be present. Intermediate maps might not be (lower case "l" has nothing*/
2517     /*  at the xheight), in which case we just ignore that map. */
2518 
2519     /* Coalesce overlapping hint zones. These won't shrink, but the counters */
2520     /*  between them will */
2521     overlaps = SCFindHintOverlaps(hints, fix->maps[0].current, fix->maps[fix->cnt-1].current, &tot, &counter_len );
2522     /* A glyph need not have any counters at all */
2523     if ( counter_len==0 ) {
2524 	free( overlaps );
2525 return;
2526     }
2527 
2528     for ( j=0; j<tot; ++j )
2529 	overlaps[j].new_start = -10000;
2530 
2531     k=-1;
2532     for ( i=0; i<fix->cnt; ++i ) {
2533 	fix->maps[i].overlap_index = -1;
2534 	for ( j=k+1; j<tot; ++j ) {
2535 	    if ( (overlaps[j].start<=fix->maps[i].current+2 && overlaps[j].stop>=fix->maps[i].current-2 ) &&
2536 		    (j==tot-1 ||
2537 		     !(overlaps[j+1].start<=fix->maps[i].current+2 && overlaps[j+1].stop>=fix->maps[i].current-2 ))) {
2538 		overlaps[j].new_start = fix->maps[i].desired + overlaps[j].start-fix->maps[i].current;
2539 		overlaps[j].new_stop  = fix->maps[i].desired + overlaps[j].stop -fix->maps[i].current;
2540 		fix->maps[i].overlap_index = j;
2541 		if ( k!=-1 ) {
2542 		    /* Position any hint overlap zones between the one we just*/
2543 		    /*  positioned, and the one we positioned just before this*/
2544 		    double osum = 0;
2545 		    for ( l=k+1; l<j; ++l )
2546 			osum += overlaps[l].stop-overlaps[l].start;
2547 		    shrink = (overlaps[j].new_start-overlaps[k].new_stop - osum) /
2548 				(overlaps[j].start -overlaps[k].stop     - osum);
2549 		    for ( l=k+1; l<j; ++l ) {
2550 			overlaps[l].new_start = overlaps[l-1].new_stop +
2551 				shrink*(overlaps[l].start - overlaps[l-1].stop);
2552 			overlaps[l].new_stop  = overlaps[l].new_start +
2553 				(overlaps[l].stop - overlaps[l].start);
2554 		    }
2555 		}
2556 		k = j;
2557 	break;
2558 	    }
2559 	}
2560 	if ( fix->maps[i].overlap_index==-1 ) {
2561 	    /* remove this mapping, doesn't correspond to anything in the */
2562 	    /*  current glyph */
2563 	    if ( i==fix->cnt-1 && k!=-1 &&
2564 		    overlaps[k].start<=fix->maps[i].current+2 &&
2565 		    overlaps[k].stop>=fix->maps[i].current-2 )
2566 		/* Ok, normally it's a bad thing if the last fix point doesn't */
2567 		/*  get mapped, but here it gets mapped to essentially the same*/
2568 		/*  place as the previous point (A glyph which is as high as */
2569 		/*  the xheight, for instance), so we can afford to ignore it */;
2570 	    else if ( i==0 || i==fix->cnt-1 )
2571 		IError("Failed to position end points in LowerCaseRemoveSpace" );
2572 	    for ( j=i+1; j<fix->cnt; ++j )
2573 		fix->maps[j-1] = fix->maps[j];
2574 	    --(fix->cnt);
2575 	    --i;		/* Try again on the new zone */
2576 	}
2577     }
2578 
2579     for ( j=0; j<tot; ++j ) {
2580 	if ( overlaps[j].new_start == -10000 ) {
2581 	    IError( "Hint zone not positioned" );
2582 return;
2583 	}
2584     }
2585 
2586     SmallCapsPlacePoints(ss,aps,coord,hints,overlaps, tot);
2587     free(overlaps);
2588 }
2589 
BuildSCLigatures(SplineChar * sc_sc,SplineChar * cap_sc,int layer,struct genericchange * genchange)2590 static void BuildSCLigatures(SplineChar *sc_sc,SplineChar *cap_sc,int layer,
2591 	struct genericchange *genchange) {
2592     static char *ligs[] = { "ff", "fi", "fl", "ffi", "ffl", "st", "st" };
2593     char *components;
2594     int width;
2595     RefChar *rlast, *r;
2596     SplineChar *rsc;
2597     char buffer[300];
2598     /* German eszet (the double s ligature) should become two small cap "S"es */
2599 
2600     if ( cap_sc->unicodeenc==0xdf )
2601 	components = "ss";
2602     else if ( cap_sc->unicodeenc>=0xfb00 && cap_sc->unicodeenc<=0xfb06 )
2603 	components = ligs[cap_sc->unicodeenc-0xfb00];
2604     else
2605 return;
2606 
2607     width=0;
2608     rlast = NULL;
2609     while ( *components!='\0' ) {
2610 	snprintf(buffer,sizeof(buffer),"%c.%s", *components, genchange->extension_for_letters );
2611 	rsc = SFGetChar(genchange->sf,-1,buffer);
2612 	if ( rsc!=NULL ) {
2613 	    r = RefCharCreate();
2614 	    free(r->layers);
2615 	    r->layers = NULL;
2616 	    r->layer_cnt = 0;
2617 	    r->sc = rsc;
2618 	    r->unicode_enc = rsc->unicodeenc;
2619 	    r->orig_pos = rsc->orig_pos;
2620 	    r->adobe_enc = getAdobeEnc(rsc->name);
2621 	    r->transform[0] = r->transform[3] = 1.0;
2622 	    r->transform[4] = width;
2623 	    width += rsc->width;
2624 	    r->next = NULL;
2625 	    SCMakeDependent(sc_sc,rsc);
2626 	    SCReinstanciateRefChar(sc_sc,r,layer);
2627 	    if ( rlast==NULL )
2628 		sc_sc->layers[layer].refs=r;
2629 	    else
2630 		rlast->next = r;
2631 	    rlast = r;
2632 	}
2633 	++components;
2634     }
2635     sc_sc->width = width;
2636     SCCharChangedUpdate(sc_sc,layer);
2637 }
2638 
FVAddSmallCaps(FontViewBase * fv,struct genericchange * genchange)2639 void FVAddSmallCaps(FontViewBase *fv, struct genericchange *genchange) {
2640     int gid, enc, cnt, ltn,crl,grk,symbols;
2641     SplineFont *sf = fv->sf;
2642     SplineChar *sc, *sc_sc, *rsc, *achar=NULL;
2643     RefChar *ref, *r;
2644     struct lookup_subtable *c2sc[4], *smcp[4];
2645     char buffer[200];
2646 
2647     if ( sf->cidmaster!=NULL )
2648 return;		/* Can't randomly add things to a CID keyed font */
2649     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL )
2650 	sc->ticked = false;
2651     cnt=ltn=crl=grk=symbols=0;
2652     memset(c2sc,0,sizeof(c2sc)); memset(smcp,0,sizeof(smcp));
2653 
2654     for ( enc=0; enc<fv->map->enccount; ++enc ) {
2655 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2656 	    if ( genchange->do_smallcap_symbols || ( sc->unicodeenc<0x10000 &&
2657 		    (isupper(sc->unicodeenc) || islower(sc->unicodeenc) ||
2658 		     (sc->unicodeenc>=0xfb00 && sc->unicodeenc<=0xfb06)) )) {
2659 		uint32 script = SCScriptFromUnicode(sc);
2660 		if ( script==CHR('l','a','t','n'))
2661 		    ++ltn, ++cnt;
2662 		else if ( script==CHR('g','r','e','k'))
2663 		    ++grk, ++cnt;
2664 		else if ( script==CHR('c','y','r','l'))
2665 		    ++crl, ++cnt;
2666 		else if ( genchange->do_smallcap_symbols )
2667 		    ++symbols, ++cnt;
2668 	    }
2669 	}
2670     }
2671     if ( cnt==0 )
2672 return;
2673 
2674     genchange->g.cnt = genchange->m.cnt+2;
2675     genchange->g.maps = malloc(genchange->g.cnt*sizeof(struct position_maps));
2676     genchange->sf     = fv->sf;
2677     genchange->layer  = fv->active_layer;
2678 
2679     MakeSCLookups(sf,c2sc,smcp,ltn,crl,grk,symbols,genchange->petite);
2680     ff_progress_start_indicator(10,_("Small Capitals"),
2681 	_("Building small capitals"),NULL,cnt,1);
2682     for ( enc=0; enc<fv->map->enccount; ++enc ) {
2683 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2684 	    if ( genchange->do_smallcap_symbols || ( sc->unicodeenc<0x10000 &&
2685 		    (isupper(sc->unicodeenc) || islower(sc->unicodeenc) ||
2686 		     (sc->unicodeenc>=0xfb00 && sc->unicodeenc<=0xfb06)) )) {
2687 		uint32 script = SCScriptFromUnicode(sc);
2688 		if ( script!=CHR('l','a','t','n') &&
2689 			script!=CHR('g','r','e','k') &&
2690 			script!=CHR('c','y','r','l') &&
2691 			!genchange->do_smallcap_symbols )
2692     continue;
2693 		if ( sc->unicodeenc<0x10000 && islower(sc->unicodeenc)) {
2694 		    sc = SFGetChar(sf,toupper(sc->unicodeenc),NULL);
2695 		    if ( sc==NULL )
2696       goto end_loop;
2697 		}
2698 		if ( sc->ticked )
2699       goto end_loop;
2700 		/* make the glyph now, even if it contains refs, because we */
2701 		/*  want to retain the encoding ordering */
2702 		sc_sc = MakeSmallCapGlyphSlot(sf,sc,script,c2sc,smcp,fv,genchange);
2703 		if ( sc_sc==NULL )
2704       goto end_loop;
2705 		if ( sc->layers[fv->active_layer].splines==NULL )
2706     continue;	/* Deal with these later */
2707 		sc->ticked = true;
2708 		if ( achar==NULL )
2709 		    achar = sc_sc;
2710 		if ( sc->unicodeenc==0xdf || (sc->unicodeenc>=0xfb00 && sc->unicodeenc<=0xfb06))
2711 		    BuildSCLigatures(sc_sc,sc,fv->active_layer,genchange);
2712 		else
2713 		    ChangeGlyph( sc_sc,sc,fv->active_layer,genchange );
2714       end_loop:
2715 		if ( !ff_progress_next())
2716     break;
2717 	    }
2718 	}
2719     }
2720     /* OK. Here we have done all the base glyphs we are going to do. Now let's*/
2721     /*  look at things which depend on references */
2722     for ( enc=0; enc<fv->map->enccount; ++enc ) {
2723 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2724 	    if ( genchange->do_smallcap_symbols || ( sc->unicodeenc<0x10000 &&
2725 		    (isupper(sc->unicodeenc) || islower(sc->unicodeenc) ||
2726 		     (sc->unicodeenc>=0xfb00 && sc->unicodeenc<=0xfb06)) )) {
2727 		uint32 script = SCScriptFromUnicode(sc);
2728 		if ( script!=CHR('l','a','t','n') &&
2729 			script!=CHR('g','r','e','k') &&
2730 			script!=CHR('c','y','r','l') &&
2731 			!genchange->do_smallcap_symbols )
2732     continue;
2733 		if ( sc->unicodeenc<0x10000 &&islower(sc->unicodeenc)) {
2734 		    sc = SFGetChar(sf,toupper(sc->unicodeenc),NULL);
2735 		    if ( sc==NULL )
2736       goto end_loop2;
2737 		}
2738 		if ( sc->layers[fv->active_layer].refs==NULL )
2739     continue;
2740 		MakeSmallCapName(buffer,sizeof(buffer),sf,sc,genchange);
2741 		sc_sc = SFGetChar(sf,-1,buffer);
2742 		if ( sc_sc==NULL )	/* Should not happen */
2743 		    sc_sc = MakeSmallCapGlyphSlot(sf,sc,script,c2sc,smcp,fv,genchange);
2744 		if ( sc_sc==NULL )
2745       goto end_loop2;
2746 		if ( achar==NULL )
2747 		    achar = sc_sc;
2748 		if ( SFGetAlternate(sf,sc->unicodeenc,sc,false)!=NULL )
2749 		    SCBuildComposit(sf,sc_sc,fv->active_layer,NULL,true,false);
2750 		if ( sc_sc->layers[fv->active_layer].refs==NULL ) {
2751 		    RefChar *rlast = NULL;
2752 		    for ( ref=sc->layers[fv->active_layer].refs; ref!=NULL; ref=ref->next ) {
2753 			MakeSmallCapName(buffer,sizeof(buffer),sf,ref->sc,genchange);
2754 			rsc = SFGetChar(sf,-1,buffer);
2755 			/* Look for grave.taboldstyle, grave.sc, and grave */
2756 			if ( rsc==NULL && isaccent(ref->sc->unicodeenc)) {
2757 			    snprintf(buffer,sizeof(buffer),"%s.%s", ref->sc->name, genchange->extension_for_letters );
2758 			    rsc = SFGetChar(sf,-1,buffer);
2759 			}
2760 			if ( rsc==NULL && isaccent(ref->sc->unicodeenc))
2761 			    rsc = ref->sc;
2762 			if ( rsc!=NULL ) {
2763 			    r = RefCharCreate();
2764 			    free(r->layers);
2765 			    *r = *ref;
2766 			    r->layers = NULL;
2767 			    r->layer_cnt = 0;
2768 			    r->next = NULL;
2769 			    r->sc = rsc;
2770 			    r->transform[4] *= genchange->hcounter_scale;
2771 			    r->transform[5] *= genchange->use_vert_mapping ? genchange->v_scale : genchange->vcounter_scale;
2772 			    SCMakeDependent(sc_sc,rsc);
2773 			    SCReinstanciateRefChar(sc_sc,r,fv->active_layer);
2774 			    if ( rlast==NULL )
2775 				sc_sc->layers[fv->active_layer].refs=r;
2776 			    else
2777 				rlast->next = r;
2778 			    rlast = r;
2779 			    sc_sc->width = rsc->width;
2780 			}
2781 		    }
2782 		    SCCharChangedUpdate(sc_sc,fv->active_layer);
2783 		}
2784       end_loop2:
2785 		if ( sc!=NULL ) {
2786 		    if ( !sc->ticked && !ff_progress_next())
2787     break;
2788 		    sc->ticked = true;
2789 		}
2790 	    }
2791 	}
2792     }
2793     ff_progress_end_indicator();
2794     if ( achar!=NULL )
2795 	FVDisplayGID(fv,achar->orig_pos);
2796     free(genchange->g.maps);
2797 }
2798 
2799 /* ************************************************************************** */
2800 /* ************************** Subscript/Superscript ************************* */
2801 /* ************************************************************************** */
2802 
MakeSupSupLookup(SplineFont * sf,uint32 feature_tag,uint32 * scripts,int scnt)2803 static struct lookup_subtable *MakeSupSupLookup(SplineFont *sf,uint32 feature_tag,
2804 	uint32 *scripts,int scnt) {
2805     OTLookup *test, *found;
2806     FeatureScriptLangList *fl;
2807     struct scriptlanglist *sl;
2808     int i;
2809     struct lookup_subtable *sub;
2810 
2811     if ( sf->cidmaster ) sf=sf->cidmaster;
2812     found = NULL;
2813     for ( i=0; i<scnt && found == NULL; ++i ) {
2814 	for ( test=sf->gsub_lookups; test!=NULL; test=test->next ) if ( test->lookup_type==gsub_single ) {
2815 	    if ( FeatureScriptTagInFeatureScriptList(feature_tag,scripts[i],test->features)) {
2816 		found = test;
2817 	break;
2818 	    }
2819 	}
2820     }
2821 
2822     if ( found==NULL ) {
2823 	sub = SFSubTableFindOrMake(sf,feature_tag,scripts[0],gsub_single);
2824 	found = sub->lookup;
2825     }
2826     fl = FindFeatureTagInFeatureScriptList(feature_tag,found->features);
2827     for ( i=0; i<scnt; ++i ) {
2828 	for ( sl=fl->scripts; sl!=NULL && sl->script!=scripts[i]; sl=sl->next );
2829 	if ( sl==NULL ) {
2830 	    sl = chunkalloc(sizeof(struct scriptlanglist));
2831 	    sl->script = scripts[i];
2832 	    sl->lang_cnt = 1;
2833 	    sl->langs[0] = DEFAULT_LANG;
2834 	    sl->next = fl->scripts;
2835 	    fl->scripts = sl;
2836 	}
2837     }
2838 
2839 return( found->subtables );
2840 }
2841 
MakeSubSupGlyphSlot(SplineFont * sf,SplineChar * sc,struct lookup_subtable * feature,FontViewBase * fv,struct genericchange * genchange)2842 static SplineChar *MakeSubSupGlyphSlot(SplineFont *sf,SplineChar *sc,
2843 	struct lookup_subtable *feature,
2844 	FontViewBase *fv, struct genericchange *genchange) {
2845     SplineChar *sc_sc;
2846     char buffer[300];
2847     PST *pst;
2848     int enc;
2849 
2850     snprintf(buffer,sizeof(buffer),"%s.%s", sc->name, genchange->glyph_extension );
2851     sc_sc = SFGetChar(sf,-1,buffer);
2852     if ( sc_sc!=NULL ) {
2853 	SCPreserveLayer(sc_sc,fv->active_layer,false);
2854 	SCClearLayer(sc_sc,fv->active_layer);
2855 return( sc_sc );
2856     }
2857     enc = SFFindSlot(sf, fv->map, -1, buffer );
2858     if ( enc==-1 )
2859 	enc = fv->map->enccount;
2860     sc_sc = SFMakeChar(sf,fv->map,enc);
2861     free(sc_sc->name);
2862     sc_sc->name = copy(buffer);
2863     SFHashGlyph(sf,sc_sc);
2864 
2865     pst = chunkalloc(sizeof(PST));
2866     pst->next = sc->possub;
2867     sc->possub = pst;
2868     pst->subtable = feature;
2869     pst->type = pst_substitution;
2870     pst->u.subs.variant = copy(buffer);
2871 
2872 return( sc_sc );
2873 }
2874 
FVGenericChange(FontViewBase * fv,struct genericchange * genchange)2875 void FVGenericChange(FontViewBase *fv, struct genericchange *genchange) {
2876     int gid, enc, cnt;
2877     SplineFont *sf = fv->sf;
2878     SplineChar *sc, *sc_sc, *rsc, *achar=NULL;
2879     RefChar *ref, *r;
2880     struct lookup_subtable *feature;
2881     char buffer[200];
2882 
2883     if ( sf->cidmaster!=NULL && genchange->gc == gc_subsuper )
2884 return;		/* Can't randomly add things to a CID keyed font */
2885 
2886     if ( genchange->small != NULL ) {
2887 	genchange->italic_angle = genchange->small->italic_angle;
2888 	genchange->tan_ia = genchange->small->tan_ia;
2889     }
2890 
2891     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL )
2892 	sc->ticked = false;
2893 
2894     cnt = 0;
2895     for ( enc=0; enc<fv->map->enccount; ++enc ) {
2896 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2897 	    ++cnt;
2898 	}
2899     }
2900     if ( cnt==0 )
2901 return;
2902 
2903     genchange->g.cnt = genchange->m.cnt+2;
2904     genchange->g.maps = malloc(genchange->g.cnt*sizeof(struct position_maps));
2905 
2906     if ( genchange->feature_tag!=0 ) {
2907 	uint32 *scripts = malloc(cnt*sizeof(uint32));
2908 	int scnt = 0;
2909 	for ( enc=0; enc<fv->map->enccount; ++enc ) {
2910 	    if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2911 		uint32 script = SCScriptFromUnicode(sc);
2912 		int i;
2913 		for ( i=0; i<scnt; ++i )
2914 		    if ( scripts[i]==script )
2915 		break;
2916 		if ( i==scnt )
2917 		    scripts[scnt++] = script;
2918 	    }
2919 	}
2920 
2921 	feature = MakeSupSupLookup(sf,genchange->feature_tag,scripts,scnt);
2922 	free(scripts);
2923     } else
2924 	feature = NULL;
2925 
2926     if ( genchange->gc==gc_subsuper )
2927 	ff_progress_start_indicator(10,_("Subscripts/Superscripts"),
2928 	    _("Building sub/superscripts"),NULL,cnt,1);
2929     else
2930 	ff_progress_start_indicator(10,_("Generic change"),
2931 	    _("Changing glyphs"),NULL,cnt,1);
2932 
2933     for ( enc=0; enc<fv->map->enccount; ++enc ) {
2934 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2935 	    if ( sc->ticked )
2936   goto end_loop;
2937 	    if ( sc->layers[fv->active_layer].splines==NULL ) {
2938 		/* Create the glyph now, so it gets encoded right, but otherwise */
2939 		/*  skip for now */
2940 		if ( genchange->glyph_extension != NULL )
2941 		    sc_sc = MakeSubSupGlyphSlot(sf,sc,feature,fv,genchange);
2942     continue;	/* Deal with these later */
2943 	    }
2944 	    sc->ticked = true;
2945 	    if ( genchange->glyph_extension != NULL ) {
2946 		sc_sc = MakeSubSupGlyphSlot(sf,sc,feature,fv,genchange);
2947 		if ( sc_sc==NULL )
2948       goto end_loop;
2949 	    } else {
2950 		sc_sc = sc;
2951 		SCPreserveLayer(sc,fv->active_layer,true);
2952 	    }
2953 	    if ( achar==NULL )
2954 		achar = sc_sc;
2955 	    ChangeGlyph( sc_sc,sc,fv->active_layer,genchange );
2956       end_loop:
2957 	    if ( !ff_progress_next())
2958     break;
2959 	}
2960     }
2961     /* OK. Here we have done all the base glyphs we are going to do. Now let's*/
2962     /*  look at things which depend on references */
2963     /* This is only relevant if we've got an extension, else we just use the */
2964     /*  same old refs */
2965     if ( genchange->glyph_extension != NULL ) for ( enc=0; enc<fv->map->enccount; ++enc ) {
2966 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
2967 	    if ( sc->layers[fv->active_layer].refs==NULL )
2968 	continue;
2969 	    snprintf(buffer,sizeof(buffer),"%s.%s", sc->name, genchange->glyph_extension );
2970 	    sc_sc = SFGetChar(sf,-1,buffer);
2971 	    if ( sc_sc==NULL )	/* Should not happen */
2972 		sc_sc = MakeSubSupGlyphSlot(sf,sc,feature,fv,genchange);
2973 	    if ( sc_sc==NULL )
2974 	  goto end_loop2;
2975 	    if ( achar==NULL )
2976 		achar = sc_sc;
2977 	    /* BuildComposite can do a better job of positioning accents */
2978 	    /*  than I'm going to do here... */
2979 	    if ( sc->layers[fv->active_layer].splines==NULL &&
2980 		    SFGetAlternate(sf,sc->unicodeenc,sc,false)!=NULL )
2981 		SCBuildComposit(sf,sc_sc,fv->active_layer,NULL,true,false);
2982 	    if ( sc_sc->layers[fv->active_layer].refs==NULL ) {
2983 		RefChar *rlast = NULL;
2984 		for ( ref=sc->layers[fv->active_layer].refs; ref!=NULL; ref=ref->next ) {
2985 		    snprintf(buffer,sizeof(buffer),"%s.%s", ref->sc->name, genchange->glyph_extension );
2986 		    rsc = SFGetChar(sf,-1,buffer);
2987 		    if ( rsc==NULL && isaccent(ref->sc->unicodeenc))
2988 			rsc = ref->sc;
2989 		    if ( rsc!=NULL ) {
2990 			r = RefCharCreate();
2991 			free(r->layers);
2992 			*r = *ref;
2993 			r->layers = NULL;
2994 			r->layer_cnt = 0;
2995 			r->next = NULL;
2996 			r->sc = rsc;
2997 			r->transform[4] *= genchange->hcounter_scale;
2998 			r->transform[5] *= genchange->use_vert_mapping ? genchange->v_scale : genchange->vcounter_scale;
2999 			if ( rsc==ref->sc )
3000 			    r->transform[5] += genchange->vertical_offset;
3001 			SCMakeDependent(sc_sc,rsc);
3002 			SCReinstanciateRefChar(sc_sc,r,fv->active_layer);
3003 			if ( rlast==NULL )
3004 			    sc_sc->layers[fv->active_layer].refs=r;
3005 			else
3006 			    rlast->next = r;
3007 			rlast = r;
3008 		    }
3009 		}
3010 		SCCharChangedUpdate(sc_sc,fv->active_layer);
3011 	    }
3012       end_loop2:
3013 	    if ( !sc->ticked && !ff_progress_next())
3014     break;
3015 	    sc->ticked = true;
3016 	}
3017     } else for ( enc=0; enc<fv->map->enccount; ++enc ) {
3018 	if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
3019 	    for ( ref=sc->layers[fv->active_layer].refs; ref!=NULL; ref=ref->next ) {
3020 		ref->transform[4] *= genchange->hcounter_scale;
3021 		ref->transform[5] *= genchange->use_vert_mapping ? genchange->v_scale : genchange->vcounter_scale;
3022 	    }
3023 	    if ( sc->layers[fv->active_layer].refs!=NULL )
3024 		SCCharChangedUpdate(sc,fv->active_layer);
3025 	}
3026     }
3027     ff_progress_end_indicator();
3028     if ( achar!=NULL )
3029 	FVDisplayGID(fv,achar->orig_pos);
3030     free(genchange->g.maps);
3031 }
3032 
CVGenericChange(CharViewBase * cv,struct genericchange * genchange)3033 void CVGenericChange(CharViewBase *cv, struct genericchange *genchange) {
3034     SplineChar *sc=cv->sc;
3035     int layer = CVLayer(cv);
3036 
3037     if ( genchange->gc != gc_generic || layer<0 )
3038 return;
3039 
3040     if ( genchange->small != NULL ) {
3041 	genchange->italic_angle = genchange->small->italic_angle;
3042 	genchange->tan_ia = genchange->small->tan_ia;
3043     }
3044 
3045     genchange->g.cnt = genchange->m.cnt+2;
3046     genchange->g.maps = malloc(genchange->g.cnt*sizeof(struct position_maps));
3047 
3048     if ( sc->layers[layer].splines!=NULL ) {
3049 	SCPreserveLayer(sc,layer,true);
3050 	ChangeGlyph( sc,sc,layer,genchange );
3051     }
3052 
3053     free(genchange->g.maps);
3054 }
3055 
SSControlStems(SplineSet * ss,double stemwidthscale,double stemheightscale,double hscale,double vscale,double xheight)3056 SplineSet *SSControlStems(SplineSet *ss,double stemwidthscale, double stemheightscale,
3057 	double hscale, double vscale, double xheight) {
3058     SplineFont dummysf;
3059     SplineChar dummy;
3060     Layer layers[2];
3061     LayerInfo li[2];
3062     struct genericchange genchange;
3063     SplineSet *spl;
3064     int order2 = 0;
3065 
3066     for ( spl=ss; spl!=NULL ; spl=spl->next ) {
3067 	if ( spl->first->next!= NULL ) {
3068 	    order2 = spl->first->next->order2;
3069     break;
3070 	}
3071     }
3072 
3073     memset(&dummysf,0,sizeof(dummysf));
3074     memset(&dummy,0,sizeof(dummy));
3075     memset(&li,0,sizeof(li));
3076     memset(&layers,0,sizeof(layers));
3077     memset(&genchange,0,sizeof(genchange));
3078 
3079     dummysf.ascent = 800; dummysf.descent = 200;
3080     dummysf.layer_cnt = 2;
3081     dummysf.layers = li;
3082     li[ly_fore].order2 = order2;
3083     dummy.parent=&dummysf;
3084     dummy.name="nameless";
3085     dummy.layer_cnt = 2;
3086     dummy.layers = layers;
3087     dummy.unicodeenc = -1;
3088     layers[ly_fore].order2 = order2;
3089     layers[ly_fore].splines = ss;
3090 
3091     if ( hscale==-1 && vscale==-1 )
3092 	hscale = vscale = 1;
3093     if ( stemwidthscale==-1 && stemheightscale==-1 )
3094 	stemwidthscale = stemheightscale = 1;
3095 
3096     genchange.stem_width_scale  = stemwidthscale !=-1 ? stemwidthscale  : stemheightscale;
3097     genchange.stem_height_scale = stemheightscale!=-1 ? stemheightscale : stemwidthscale ;
3098     genchange.hcounter_scale    = hscale !=-1 ? hscale  : vscale;
3099     genchange.v_scale	   = vscale !=-1 ? vscale  : hscale;
3100     genchange.lsb_scale = genchange.rsb_scale = genchange.hcounter_scale;
3101 
3102     ChangeGlyph( &dummy,&dummy,ly_fore,&genchange );
3103 return( ss );
3104 }
3105 
3106 /* ************************************************************************** */
3107 /* ***************************** Condense/Extend **************************** */
3108 /* ************************************************************************** */
3109 
3110 /* We need to look at the counters. There are two types of counters: */
3111 /*  the left and right side bearings */
3112 /*  internal counters		*/
3113 /* If the glyph is nicely hinted with vertical stems then all we need to */
3114 /*  do is look at the hints. Complications "B" which has slightly different */
3115 /*  counters top and bottom. */
3116 /* I'm going to assume that LCG glyphs have at most two counter zones, one */
3117 /*  near the bottom (baseline), one near the top */
3118 /* However many glyphs have diagonal stems: V, A, W, M, K, X, Y */
3119 /*  Many of these have two zones (like "B" above) W, M, K, Y (maybe X) */
3120 /* Find the places where these guys hit the baseline/cap-height (x-height) */
3121 /*  and define these as potential counter boundries. Ignore places where   */
3122 /*  glyphs hit with curves (like O, Q, p). */
3123 /* Remember to merge a hint with a top/bottom hit (harder with serifs) */
3124 
3125 /* We may still not have a useful counter: 7 3 2 C E T */
3126 /*  Use the left and right sides of the bounding box (do I need to know */
3127 /*  StemSnapV? -- standard stem size -- yes, don't want to expand the stem) */
3128 /* Don't try to make I 1 l i grow, only one stem even if the serifs make the */
3129 /*  bounding box bigger */
3130 
3131 /* If the font is italic, then skew it by the italic angle in hopes of getting*/
3132 /*  some real vertical stems, rehint, condense/extend & unskew */
3133 
SFStdVW(SplineFont * sf)3134 double SFStdVW(SplineFont *sf) {
3135     double stdvw = 0;
3136     char *ret;
3137 
3138     if ( sf->private!=NULL && (ret=PSDictHasEntry(sf->private,"StdVW"))!=NULL )
3139 	stdvw = strtod(ret,NULL);
3140 
3141     if ( stdvw<=0 )
3142 	stdvw = (sf->ascent+sf->descent)/12.5;
3143 return( stdvw );
3144 }
3145 
CIAdd(struct counterinfo * ci,int z,double start,double width)3146 static void CIAdd(struct counterinfo *ci,int z,double start,double width) {
3147     int i, j;
3148 
3149     if ( width<0 ) {
3150 	start += width;
3151 	width = -width;
3152     }
3153     for ( i = 0; i<ci->cnts[z]; ++i ) {
3154 	if ( start+width<ci->zones[z][i].start )
3155     break;
3156 	if ( start<ci->zones[z][i].start + ci->zones[z][i].width )
3157 return;		/* It intersects something that's already there */
3158 		/* Assume the previous entry came from a hint and */
3159 		/* so specifies the stem without the serifs and is better */
3160     }
3161 
3162     /* Need to add */
3163     if ( ci->cnts[z]>=ci->maxes[z] )
3164 	ci->zones[z] = realloc(ci->zones[z],(ci->maxes[z]+=10)*sizeof(struct ci_zones));
3165     for ( j=ci->cnts[z]; j>i; --j )
3166 	ci->zones[z][j] = ci->zones[z][j-1];
3167     ci->zones[z][i].start = ci->zones[z][i].moveto   = start;
3168     ci->zones[z][i].width = ci->zones[z][i].newwidth = width;
3169     ++ ci->cnts[z];
3170 }
3171 
SpOnEdge(SplinePoint * sp,double y,int dir,struct counterinfo * ci,int z)3172 static int SpOnEdge(SplinePoint *sp,double y,int dir,struct counterinfo *ci,int z) {
3173     SplinePoint *nsp, *nnsp, *psp;
3174 
3175     if ( sp->me.y<=y-1 || sp->me.y>y+1 )
3176 return( false );
3177 
3178     /* We've already checked that we have a closed contour, so we don't need */
3179     /*  to worry that something might be NULL */
3180     psp = sp->prev->from;		/* the previous point must not be near the y value */
3181     if (( psp->me.y>y-1 && psp->me.y<=y+1 ) ||
3182 	    ( dir>0 && psp->me.y<=y ) ||
3183 	    ( dir<0 && psp->me.y>=y ) )
3184 return( true );				/* But the point itself was on the edge */
3185 
3186     /* Either the next point is on the edge too, or we can have a dished */
3187     /*  serif, where the next point is off the edge, but the one after is on */
3188     /*  In a TrueType font there may be several such points, but for a PS */
3189     /*  flex hint there will be only one */
3190     nsp = sp->next->to;
3191     while ( nsp!=sp &&
3192 	    ((dir>0 && nsp->me.y>y+1 && nsp->me.y<y+10) ||
3193 	     (dir<0 && nsp->me.y<y-1 && nsp->me.y>y-10)) )
3194 	nsp = nsp->next->to;
3195     if ( nsp==sp )
3196 return( true );
3197     if ( nsp->me.y<=y-1 || nsp->me.y>y+1 )
3198 return( true );
3199     nnsp = nsp->next->to;
3200     if (( nnsp->me.y>y-1 && nnsp->me.y<=y+1 ) ||
3201 	( dir>0 && nnsp->me.y<=y ) ||
3202 	( dir<0 && nnsp->me.y>=y ) )
3203 return( true );
3204 
3205     if ( nsp->me.x-sp->me.x > 3.5 * ci->stdvw || nsp->me.x-sp->me.x < -3.5*ci->stdvw )
3206 return( true );
3207     CIAdd(ci,z,sp->me.x,nsp->me.x-sp->me.x);
3208 return( true );
3209 }
3210 
HintActiveAt(StemInfo * h,double y)3211 static int HintActiveAt(StemInfo *h,double y) {
3212     HintInstance *hi;
3213 
3214     for ( hi=h->where; hi!=NULL; hi=hi->next ) {
3215 	if ( y>=hi->begin && y<=hi->end )
3216 return( true );
3217     }
3218 return( false );
3219 }
3220 
PerGlyphFindCounters(struct counterinfo * ci,SplineChar * sc,int layer)3221 static void PerGlyphFindCounters(struct counterinfo *ci,SplineChar *sc, int layer) {
3222     StemInfo *h;
3223     double y, diff;
3224     int i,z;
3225     DBounds b;
3226     SplineSet *ss;
3227     SplinePoint *sp;
3228 
3229     ci->sc = sc;
3230     ci->layer = layer;
3231     ci->bottom_y = 0;
3232     if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && isupper(sc->unicodeenc))
3233 	ci->top_y = ci->bd.caph>0?ci->bd.caph:4*sc->parent->ascent/5;
3234     else
3235 	ci->top_y = ci->bd.xheight>0?ci->bd.xheight:sc->parent->ascent/2;
3236     ci->boundry = ci->top_y/2;
3237     ci->has_two_zones = false;
3238     ci->cnts[0] = ci->cnts[1] = 0;
3239 
3240     diff = (ci->top_y - ci->bottom_y)/16.0;
3241     for ( h=sc->vstem; h!=NULL; h=h->next ) {
3242 	for ( i=1, y=ci->bottom_y+diff; i<16; ++i, y+=diff ) {
3243 	    if ( HintActiveAt(h,y)) {
3244 		if ( i<8 ) {
3245 		    CIAdd(ci,BOT_Z,h->start,h->width);
3246 		    y += (7-i)*diff;
3247 		    i = 7;
3248 		} else if ( i==8 ) {
3249 		    CIAdd(ci,BOT_Z,h->start,h->width);
3250 		    CIAdd(ci,TOP_Z,h->start,h->width);
3251 	break;
3252 		} else {
3253 		    CIAdd(ci,TOP_Z,h->start,h->width);
3254 	break;
3255 		}
3256 	    }
3257 	}
3258     }
3259 
3260     for ( ss = sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
3261 	if ( ss->first->prev==NULL )
3262     continue;
3263 	for ( sp=ss->first; ; ) {
3264 	    if ( SpOnEdge(sp,ci->bottom_y,1,ci,BOT_Z)) {
3265 		/* All Done */
3266 		;
3267 	    } else if ( SpOnEdge(sp,ci->top_y,-1,ci,TOP_Z)) {
3268 		/* All Done */
3269 		;
3270             }
3271 	    /* Checked for sp->next==NULL above loop */
3272 	    sp = sp->next->to;
3273 	    if ( sp==ss->first )
3274 	break;
3275 	}
3276     }
3277 
3278     SplineSetFindBounds(sc->layers[layer].splines,&b);
3279     ci->bb = b;
3280     if ( ci->cnts[0]<2 && ci->cnts[1]<2 ) {
3281 	if ( b.maxx - b.minx > 4*ci->stdvw ) {
3282 	    for ( i=0; i<2; ++i ) {
3283 		CIAdd(ci,i,b.minx,1.5*ci->stdvw);
3284 		CIAdd(ci,i,b.maxx-1.5*ci->stdvw, 1.5*ci->stdvw);
3285 	    }
3286 	}
3287     }
3288 
3289     if ( ci->cnts[0]!=ci->cnts[1] )
3290 	ci->has_two_zones = true;
3291     else {
3292 	for ( i=0; i<ci->cnts[0]; ++i ) {
3293 	    /* if one stem is entirely within the other, then that counts as */
3294 	    /*  the same */
3295 	    if (( ci->zones[0][i].start<=ci->zones[1][i].start &&
3296 		    ci->zones[0][i].start+ci->zones[0][i].width >= ci->zones[1][i].start+ci->zones[1][i].width) ||
3297 		  ( ci->zones[1][i].start<=ci->zones[0][i].start &&
3298 		    ci->zones[1][i].start+ci->zones[1][i].width >= ci->zones[0][i].start+ci->zones[0][i].width) )
3299 	continue;		/* They match, close enough */
3300 	    ci->has_two_zones = true;
3301 	break;
3302 	}
3303     }
3304 
3305     /* We do not include the side bearing adjustment here. That must be done  */
3306     /*  separately, because we skip counter movements if there are no counters*/
3307     for ( z=0; z<2; ++z ) {
3308 	for ( i=1; i<ci->cnts[z]; ++i ) {
3309 	    ci->zones[z][i].moveto = ci->zones[z][i-1].moveto + ci->zones[z][i-1].newwidth +
3310 		    ci->c_factor/100.0 * (ci->zones[z][i].start-(ci->zones[z][i-1].start+ci->zones[z][i-1].width)) +
3311 		    ci->c_add;
3312 	}
3313     }
3314 
3315     if ( ci->has_two_zones ) {
3316 	int j,k;
3317 	double diff;
3318 	/* Now are there any common stems in the two zones? Common stems */
3319 	/*  should be forced to the same location even if that isn't what */
3320 	/*  we calculated above */
3321 	for ( i=0; i<ci->cnts[0]; ++i ) {
3322 	    for ( j=0; j<ci->cnts[1]; ++j ) {
3323 		if ( ci->zones[0][i].start == ci->zones[1][j].start &&
3324 			ci->zones[0][i].moveto != ci->zones[1][j].moveto ) {
3325 		    if ( ci->zones[0][i].moveto > ci->zones[1][j].moveto ) {
3326 			diff = ci->zones[0][i].moveto - ci->zones[1][j].moveto;
3327 			for ( k=j; k<ci->cnts[1]; ++k )
3328 			    ci->zones[1][j].moveto += diff;
3329 		    } else {
3330 			diff = ci->zones[1][j].moveto - ci->zones[0][i].moveto;
3331 			for ( k=j; k<ci->cnts[0]; ++k )
3332 			    ci->zones[0][i].moveto += diff;
3333 		    }
3334 		}
3335 	    }
3336 	}
3337     }
3338 }
3339 
BPAdjustCEZ(BasePoint * bp,struct counterinfo * ci,int z)3340 static void BPAdjustCEZ(BasePoint *bp, struct counterinfo *ci, int z) {
3341     int i;
3342 
3343     if ( ci->cnts[z]<2 )	/* No counters */
3344 return;
3345     if ( bp->x<ci->zones[z][0].start+ci->zones[z][0].width ) {
3346 	if ( bp->x<ci->zones[z][0].start || ci->zones[z][0].width==ci->zones[z][0].newwidth )
3347 	    bp->x += ci->zones[z][0].moveto - ci->zones[z][0].start;
3348 	else
3349 	    bp->x = ci->zones[z][0].moveto +
3350 		    ci->zones[z][0].newwidth * (bp->x-ci->zones[z][0].start)/ ci->zones[z][0].width;
3351 return;
3352     }
3353 
3354     for ( i=1; i<ci->cnts[z]; ++i ) {
3355 	if ( bp->x<ci->zones[z][i].start+ci->zones[z][i].width ) {
3356 	    if ( bp->x<ci->zones[z][i].start ) {
3357 		double base = ci->zones[z][i-1].moveto + ci->zones[z][i-1].newwidth;
3358 		double oldbase = ci->zones[z][i-1].start + ci->zones[z][i-1].width;
3359 		bp->x = base +
3360 			(bp->x-oldbase) *
3361 				(ci->zones[z][i].moveto-base)/
3362 				(ci->zones[z][i].start-oldbase);
3363 	    } else {
3364 		bp->x = ci->zones[z][i].moveto +
3365 		    ci->zones[z][i].newwidth * (bp->x-ci->zones[z][i].start)/ ci->zones[z][i].width;
3366 	    }
3367 return;
3368 	}
3369     }
3370 
3371     bp->x += ci->zones[z][i-1].moveto + ci->zones[z][i-1].newwidth -
3372 	    (ci->zones[z][i-1].start + ci->zones[z][i-1].width);
3373 }
3374 
BPAdjustCE(BasePoint * bp,struct counterinfo * ci)3375 static void BPAdjustCE(BasePoint *bp, struct counterinfo *ci) {
3376 
3377     if ( !ci->has_two_zones )
3378 	BPAdjustCEZ(bp,ci,0);
3379     else if ( ci->cnts[BOT_Z]<2 && ci->cnts[TOP_Z]>=2 )
3380 	BPAdjustCEZ(bp,ci,TOP_Z);
3381     else if ( ci->cnts[TOP_Z]<2 && ci->cnts[BOT_Z]>=2 )
3382 	BPAdjustCEZ(bp,ci,BOT_Z);
3383     else if ( bp->y > ci->boundry )
3384 	BPAdjustCEZ(bp,ci,TOP_Z);
3385     else
3386 	BPAdjustCEZ(bp,ci,BOT_Z);
3387 }
3388 
CI_Init(struct counterinfo * ci,SplineFont * sf)3389 void CI_Init(struct counterinfo *ci,SplineFont *sf) {
3390 
3391     QuickBlues(sf, ci->layer, &ci->bd);
3392 
3393     ci->stdvw = SFStdVW(sf);
3394 }
3395 
SCCondenseExtend(struct counterinfo * ci,SplineChar * sc,int layer,int do_undoes)3396 void SCCondenseExtend(struct counterinfo *ci,SplineChar *sc, int layer,
3397 	int do_undoes) {
3398     SplineSet *ss;
3399     SplinePoint *sp;
3400     Spline *s, *first;
3401     DBounds b;
3402     int width;
3403     double offset;
3404     real transform[6];
3405     int order2 = sc->layers[layer].order2;
3406 
3407     if ( do_undoes )
3408 	SCPreserveLayer(sc,layer,false);
3409 
3410     if ( ci->correct_italic && sc->parent->italicangle!=0 ) {
3411 	memset(transform,0,sizeof(transform));
3412 	transform[0] = transform[3] = 1;
3413 	transform[2] = tan( sc->parent->italicangle * FF_PI/180.0 );
3414 	SplinePointListTransform(sc->layers[layer].splines,transform,tpt_AllPoints);
3415 	StemInfosFree(sc->vstem); sc->vstem=NULL;
3416     }
3417     if ( sc->vstem==NULL )
3418 	_SplineCharAutoHint(sc,ci->layer,&ci->bd,NULL,false);
3419 
3420     PerGlyphFindCounters(ci,sc, layer);
3421 
3422     for ( ss = sc->layers[layer].splines; ss!=NULL; ss = ss->next ) {
3423 	for ( sp=ss->first; ; ) {
3424 	    BPAdjustCE(&sp->nextcp,ci);
3425 	    BPAdjustCE(&sp->prevcp,ci);
3426 	    if ( sp->ttfindex==0xffff && order2 ) {
3427 		sp->me.x = (sp->nextcp.x+sp->prevcp.x)/2;
3428 		sp->me.y = (sp->nextcp.y+sp->prevcp.y)/2;
3429 	    } else
3430 		BPAdjustCE(&sp->me,ci);
3431 	    if ( sp->next==NULL )
3432 	break;
3433 	    sp = sp->next->to;
3434 	    if ( sp==ss->first )
3435 	break;
3436 	}
3437 	first = NULL;
3438 	for ( s=ss->first->next; s!=NULL && s!=first; s = s->to->next ) {
3439 	    if ( first==NULL ) first = s;
3440 	    SplineRefigure(s);
3441 	}
3442     }
3443 
3444     SplineSetFindBounds(sc->layers[layer].splines,&b);
3445     memset(transform,0,sizeof(transform));
3446     transform[0] = transform[3] = 1;
3447     transform[4] = ci->bb.minx*ci->sb_factor/100. + ci->sb_add - b.minx;
3448     if ( transform[4]!=0 )
3449 	SplinePointListTransform(sc->layers[layer].splines,transform,tpt_AllPoints);
3450     if ( layer!=ly_back ) {
3451 	width = b.maxx + (sc->width-ci->bb.maxx)*ci->sb_factor/100. + ci->sb_add;
3452 	SCSynchronizeWidth(sc,width,sc->width,NULL);
3453 	offset = (b.maxx-b.minx)/2 - (ci->bb.maxx-ci->bb.minx)/2;
3454 	/* We haven't really changed the left side bearing by offset, but */
3455 	/*  this is the amount (about) by which we need to adjust accents */
3456 	SCSynchronizeLBearing(sc,offset,ci->layer);
3457     }
3458 
3459     if ( ci->correct_italic && sc->parent->italicangle!=0 ) {
3460 	/* If we unskewed it, we want to skew it now */
3461 	memset(transform,0,sizeof(transform));
3462 	transform[0] = transform[3] = 1;
3463 	transform[2] = -tan( sc->parent->italicangle * FF_PI/180.0 );
3464 	SplinePointListTransform(sc->layers[layer].splines,transform,tpt_AllPoints);
3465     }
3466 
3467     if ( layer!=ly_back ) {
3468 	/* Hints will be inccorrect (misleading) after these transformations */
3469 	StemInfosFree(sc->vstem); sc->vstem=NULL;
3470 	StemInfosFree(sc->hstem); sc->hstem=NULL;
3471 	DStemInfosFree(sc->dstem); sc->dstem=NULL;
3472 	SCOutOfDateBackground(sc);
3473     }
3474     SCCharChangedUpdate(sc,layer);
3475 }
3476 
ScriptSCCondenseExtend(SplineChar * sc,struct counterinfo * ci)3477 void ScriptSCCondenseExtend(SplineChar *sc,struct counterinfo *ci) {
3478 
3479     SCCondenseExtend(ci, sc, ci->layer, true);
3480 
3481     free( ci->zones[0]);
3482     free( ci->zones[1]);
3483 }
3484 
FVCondenseExtend(FontViewBase * fv,struct counterinfo * ci)3485 void FVCondenseExtend(FontViewBase *fv,struct counterinfo *ci) {
3486     int i, gid;
3487     SplineChar *sc;
3488 
3489     for ( i=0; i<fv->map->enccount; ++i ) if ( fv->selected[i] &&
3490 	    (gid = fv->map->map[i])!=-1 && (sc=fv->sf->glyphs[gid])!=NULL ) {
3491 	SCCondenseExtend(ci,sc,ly_fore,true);
3492     }
3493 
3494     free( ci->zones[0]);
3495     free( ci->zones[1]);
3496 }
3497 
3498 /* ************************************************************************** */
3499 /* ***************************** Embolden Dialog **************************** */
3500 /* ************************************************************************** */
3501 
3502 
3503 struct ptmoves {
3504     SplinePoint *sp;
3505     BasePoint pdir, ndir;
3506     double factor;
3507     BasePoint newpos;
3508     uint8 touched;
3509 };
3510 
MaxContourCount(SplineSet * ss)3511 static int MaxContourCount(SplineSet *ss) {
3512     int cnt, ccnt;
3513     SplinePoint *sp;
3514 
3515     ccnt = 0;
3516     for ( ; ss!=NULL ; ss=ss->next ) {
3517 	if ( ss->first->prev==NULL )
3518     continue;
3519 	for ( cnt=0, sp=ss->first; ; ) {
3520 	    sp = sp->next->to; ++cnt;
3521 	    if ( sp==ss->first )
3522 	break;
3523 	}
3524 	if ( cnt>ccnt ) ccnt = cnt;
3525     }
3526 return( ccnt );
3527 }
3528 
PtMovesInitToContour(struct ptmoves * ptmoves,SplineSet * ss)3529 static int PtMovesInitToContour(struct ptmoves *ptmoves,SplineSet *ss) {
3530     int cnt;
3531     SplinePoint *sp;
3532     BasePoint dir1, dir2;
3533     double len;
3534 
3535     for ( cnt=0, sp=ss->first; ; ) {
3536 	ptmoves[cnt].sp = sp;
3537 	ptmoves[cnt].newpos = sp->me;
3538 	ptmoves[cnt].touched = false;
3539 	if ( sp->nonextcp ) {
3540 	    dir1.x = sp->next->to->me.x - sp->me.x;
3541 	    dir1.y = sp->next->to->me.y - sp->me.y;
3542 	} else {
3543 	    dir1.x = sp->nextcp.x - sp->me.x;
3544 	    dir1.y = sp->nextcp.y - sp->me.y;
3545 	}
3546 	len = dir1.x*dir1.x + dir1.y*dir1.y;
3547 	if ( len!=0 ) {
3548 	    len = sqrt(len);
3549 	    dir1.x /= len;
3550 	    dir1.y /= len;
3551 	}
3552 	ptmoves[cnt].ndir = dir1;
3553 	if ( dir1.x<0 ) dir1.x = -dir1.x;
3554 
3555 	if ( sp->noprevcp ) {
3556 	    dir2.x = sp->prev->from->me.x - sp->me.x;
3557 	    dir2.y = sp->prev->from->me.y - sp->me.y;
3558 	} else {
3559 	    dir2.x = sp->prevcp.x - sp->me.x;
3560 	    dir2.y = sp->prevcp.y - sp->me.y;
3561 	}
3562 	len = dir2.x*dir2.x + dir2.y*dir2.y;
3563 	if ( len!=0 ) {
3564 	    len = sqrt(len);
3565 	    dir2.x /= len;
3566 	    dir2.y /= len;
3567 	}
3568 	ptmoves[cnt].pdir = dir2;
3569 	if ( dir2.x<0 ) dir2.x = -dir2.x;
3570 
3571 	ptmoves[cnt].factor = dir1.x>dir2.x ? dir1.x : dir2.x;
3572 
3573 	sp = sp->next->to; ++cnt;
3574 	if ( sp==ss->first )
3575     break;
3576     }
3577     ptmoves[cnt] = ptmoves[0];	/* Life is easier if we don't have to worry about edge effects */
3578 return( cnt );
3579 }
3580 
InterpolateControlPointsAndSet(struct ptmoves * ptmoves,int cnt)3581 static void InterpolateControlPointsAndSet(struct ptmoves *ptmoves,int cnt) {
3582     SplinePoint *sp, *nsp;
3583     int i;
3584     int order2 = ptmoves[0].sp->next!=NULL && ptmoves[0].sp->next->order2;
3585 
3586     ptmoves[cnt].newpos = ptmoves[0].newpos;
3587     for ( i=0; i<cnt; ++i ) {
3588 	sp = ptmoves[i].sp;
3589 	nsp = ptmoves[i+1].sp;
3590 	if ( sp->nonextcp )
3591 	    sp->nextcp = ptmoves[i].newpos;
3592 	if ( nsp->noprevcp )
3593 	    nsp->prevcp = ptmoves[i+1].newpos;
3594 	if ( isnan(ptmoves[i].newpos.y) )
3595 	    IError( "Nan value in InterpolateControlPointsAndSet\n" );
3596 	if ( sp->me.y!=nsp->me.y ) {
3597 	    sp->nextcp.y = ptmoves[i].newpos.y + (sp->nextcp.y-sp->me.y)*
3598 				(ptmoves[i+1].newpos.y - ptmoves[i].newpos.y)/
3599 				(nsp->me.y - sp->me.y);
3600 	    nsp->prevcp.y = ptmoves[i].newpos.y + (nsp->prevcp.y-sp->me.y)*
3601 				(ptmoves[i+1].newpos.y - ptmoves[i].newpos.y)/
3602 				(nsp->me.y - sp->me.y);
3603 	    if ( sp->me.x!=nsp->me.x ) {
3604 		sp->nextcp.x = ptmoves[i].newpos.x + (sp->nextcp.x-sp->me.x)*
3605 				    (ptmoves[i+1].newpos.x - ptmoves[i].newpos.x)/
3606 				    (nsp->me.x - sp->me.x);
3607 		nsp->prevcp.x = ptmoves[i].newpos.x + (nsp->prevcp.x-sp->me.x)*
3608 				    (ptmoves[i+1].newpos.x - ptmoves[i].newpos.x)/
3609 				    (nsp->me.x - sp->me.x);
3610 	    }
3611 	}
3612 	if ( isnan(sp->nextcp.y) )
3613 	    IError( "Nan value in InterpolateControlPointsAndSet\n" );
3614     }
3615     for ( i=0; i<cnt; ++i )
3616 	ptmoves[i].sp->me = ptmoves[i].newpos;
3617     if ( order2 ) {
3618 	for ( i=0; i<cnt; ++i ) if ( (sp = ptmoves[i].sp)->ttfindex==0xffff ) {
3619 	    sp->me.x = (sp->nextcp.x+sp->prevcp.x)/2;
3620 	    sp->me.y = (sp->nextcp.y+sp->prevcp.y)/2;
3621 	}
3622     }
3623     for ( i=0; i<cnt; ++i )
3624 	SplineRefigure(ptmoves[i].sp->next);
3625 }
3626 
CorrectLeftSideBearing(SplineSet * ss_expanded,SplineChar * sc,int layer)3627 static void CorrectLeftSideBearing(SplineSet *ss_expanded,SplineChar *sc,int layer) {
3628     real transform[6];
3629     DBounds old, new;
3630     /* Now correct the left side bearing */
3631 
3632     SplineSetFindBounds(sc->layers[layer].splines,&old);
3633     SplineSetFindBounds(ss_expanded,&new);
3634     memset(transform,0,sizeof(transform));
3635     transform[0] = transform[3] = 1;
3636     transform[4] = old.minx - new.minx;
3637     if ( transform[4]!=0 ) {
3638 	SplinePointListTransform(ss_expanded,transform,tpt_AllPoints);
3639 	if ( layer==ly_fore )
3640 	    SCSynchronizeLBearing(sc,transform[4],layer);
3641     }
3642 }
3643 
FindMatchingPoint(int ptindex,SplineSet * ss)3644 static SplinePoint *FindMatchingPoint(int ptindex,SplineSet *ss) {
3645     SplinePoint *sp;
3646 
3647     if( ptindex==0 )
3648 return( NULL );
3649     for ( ; ss!=NULL; ss=ss->next ) {
3650 	for ( sp=ss->first; ; ) {
3651 	    if ( sp->ptindex == ptindex )
3652 return( sp );
3653 	    if ( sp->next == NULL )
3654 	break;
3655 	    sp = sp->next->to;
3656 	    if ( sp==ss->first )
3657 	break;
3658 	}
3659     }
3660 return( NULL );
3661 }
3662 
OnHint(StemInfo * stems,double searchy,double * othery)3663 static StemInfo *OnHint(StemInfo *stems,double searchy,double *othery) {
3664     StemInfo *h;
3665 
3666     for ( h=stems; h!=NULL; h=h->next ) {
3667 	if ( h->start == searchy ) {
3668 	    *othery = h->start+h->width;
3669 return( h );
3670 	} else if ( h->start+h->width == searchy ) {
3671 	    *othery = h->start;
3672 return( h );
3673 	}
3674     }
3675 
3676     for ( h=stems; h!=NULL; h=h->next ) {
3677 	if ( searchy>=h->start-2 && searchy<=h->start+2 ) {
3678 	    *othery = h->start+h->width;
3679 return( h );
3680 	} else if ( searchy>=h->start+h->width-2 && searchy<=h->start+h->width+2 ) {
3681 	    *othery = h->start;
3682 return( h );
3683 	}
3684     }
3685 
3686 return( NULL );
3687 }
3688 
MightBeOnHint(StemInfo * stems,struct lcg_zones * zones,struct ptmoves * pt,double * othery)3689 static StemInfo *MightBeOnHint(StemInfo *stems,struct lcg_zones *zones,
3690 	struct ptmoves *pt,double *othery) {
3691     double offset;
3692 
3693     if ( pt->ndir.y!=0 && pt->pdir.y!=0 )
3694 return( NULL );			/* Not horizontal, not on a stem */
3695 
3696     offset = (pt->ndir.y==0 && pt->ndir.x>0) ||
3697 	    (pt->pdir.y==0 && pt->pdir.x<0 ) ?  zones->stroke_width/2
3698 					     : -zones->stroke_width/2;
3699 return( OnHint(stems,pt->sp->me.y-offset,othery));
3700 }
3701 
InHintAroundZone(StemInfo * stems,double searchy,double contains_y)3702 static int InHintAroundZone(StemInfo *stems,double searchy,double contains_y) {
3703     StemInfo *h;
3704 
3705     for ( h=stems; h!=NULL; h=h->next ) {
3706 	if ( h->start >= searchy && h->start+h->width<=searchy &&
3707 		 h->start >= contains_y && h->start+h->width<=contains_y )
3708 return( true );
3709     }
3710 return( false );
3711 }
3712 
IsStartPoint(SplinePoint * sp,SplineChar * sc,int layer)3713 static int IsStartPoint(SplinePoint *sp, SplineChar *sc, int layer) {
3714     SplineSet *ss;
3715 
3716     if ( sp->ptindex==0 )
3717 return( false );
3718     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
3719 	if ( ss->first->ptindex==sp->ptindex )
3720 return( true );
3721     }
3722 return( false );
3723 }
3724 
FindStartPoint(SplineSet * ss_expanded,SplineChar * sc,int layer)3725 static void FindStartPoint(SplineSet *ss_expanded, SplineChar *sc, int layer) {
3726     SplinePoint *sp;
3727     SplineSet *ss;
3728 
3729     for ( ss=ss_expanded; ss!=NULL; ss=ss->next ) {
3730 	int found = false;
3731 	if ( ss->first->prev==NULL )
3732     continue;
3733 	for ( sp=ss->first; ; ) {
3734 	    if ( IsStartPoint(sp,sc,layer) ) {
3735 		ss->first = ss->last = sp;
3736 		ss->start_offset = 0;
3737 		found = true;
3738 	break;
3739 	    }
3740 	    sp = sp->prev->from;
3741 	    if ( sp==ss->first )
3742 	break;
3743 	}
3744 	if ( !found ) {
3745 	    ss->first = ss->last = sp->prev->from;		/* Often true */
3746 	    ss->start_offset = 0;
3747 	}
3748     }
3749 }
3750 
LCG_EmboldenHook(SplineSet * ss_expanded,struct lcg_zones * zones,SplineChar * sc,int layer)3751 static SplineSet *LCG_EmboldenHook(SplineSet *ss_expanded,struct lcg_zones *zones,
3752 	SplineChar *sc, int layer) {
3753     SplineSet *ss;
3754     SplinePoint *sp, *nsp, *psp;
3755     int cnt, ccnt, i;
3756     struct ptmoves *ptmoves;
3757     /* When we do an expand stroke we expect that a glyph's bounding box will */
3758     /*  increase by half the stroke width in each direction. Now we want to do*/
3759     /*  different operations in each dimension. We don't want to increase the */
3760     /*  x-height, or the cap height, whereas it is ok to make the glyph wider */
3761     /*  but it would be nice to preserve the left and right side bearings. */
3762     /* So shift the glyph over by width/2 (this preserves the left side bearing)*/
3763     /* Increment the width by width (this preserves the right side bearing)   */
3764     /* The y dimension is more complex. In a normal latin (greek, cyrillic)   */
3765     /*  font the x-height is key, in a font with only capital letters it would*/
3766     /*  be the cap-height. In the subset of the font containing superscripts  */
3767     /*  we might want to key off of the superscript size. The expansion will  */
3768     /*  mean that the glyph protrudes width/2 below the baseline, and width/2 */
3769     /*  above the x-height and cap-height. Neither of those is acceptable. So */
3770     /*  choose some intermediate level, say x-height/2, any point above this  */
3771     /*  moves down by width/2, any point below this moves up by width/2 */
3772     /* That'll work for many glyphs. But consider "o", depending on how it is */
3773     /*  designed we might be moving the leftmost point either up or down when */
3774     /*  it should remain near the center. So perhaps we have a band which is  */
3775     /*  width wide right around x-height/2 and points in that band don't move */
3776     /* Better. Consider a truetype "o" were there will be intermediate points */
3777     /*  Between the top-most and left-most. These shouldn't move by the full */
3778     /*  width/2. They should move by some interpolated amount. Based on the */
3779     /*  center of the glyph? That would screw up "h". Take the normal to the */
3780     /*  curve. Dot it into the y direction. Move the point by that fraction */
3781     /* Control points should be interpolated between the movements of their */
3782     /*  on-curve points */
3783 
3784     ccnt = MaxContourCount(ss_expanded);
3785     if ( ccnt==0 )
3786 return(ss_expanded);			/* No points? Nothing to do */
3787     ptmoves = malloc((ccnt+1)*sizeof(struct ptmoves));
3788     for ( ss = ss_expanded; ss!=NULL ; ss=ss->next ) {
3789 	if ( ss->first->prev==NULL )
3790     continue;
3791 	cnt = PtMovesInitToContour(ptmoves,ss);
3792 	for ( i=0; i<cnt; ++i ) {
3793 	    int p = i==0?cnt-1:i-1, sign = 0;
3794 	    sp = ptmoves[i].sp;
3795 	    nsp = ptmoves[i+1].sp;
3796 	    psp = ptmoves[p].sp;
3797 	    if ( sp->me.y>=zones->top_zone )
3798 		sign = -1;
3799 	    else if ( sp->me.y<zones->bottom_zone )
3800 		sign = 1;
3801 
3802 	    /* Fix vertical serifs */
3803 	    if ( zones->serif_height>0 &&
3804 		     RealWithin(sp->me.y,zones->bottom_bound+zones->serif_height+zones->stroke_width/2,zones->serif_fuzz) ) {
3805 		ptmoves[i].newpos.y = zones->bottom_bound+zones->serif_height;
3806 	    } else if ( zones->serif_height>0 &&
3807 		     RealWithin(sp->me.y,zones->top_bound-zones->serif_height-zones->stroke_width/2,zones->serif_fuzz) ) {
3808 		ptmoves[i].newpos.y = zones->top_bound-zones->serif_height;
3809 	    } else if ( sign ) {
3810 		ptmoves[i].newpos.y += sign*zones->stroke_width*ptmoves[i].factor/2;
3811 		/* This is to improve the looks of diagonal stems */
3812 		if ( sp->next->islinear && sp->prev->islinear && nsp->next->islinear &&
3813 			nsp->me.y == sp->me.y &&
3814 			ptmoves[i].pdir.x*ptmoves[i+1].ndir.x + ptmoves[i].pdir.y*ptmoves[i+1].ndir.y<-.999 ) {
3815 		    if ( ptmoves[i].pdir.y<0 )
3816 			sign = -sign;
3817 		    ptmoves[i].newpos.x += sign*zones->stroke_width*ptmoves[i].pdir.x;
3818 		} else if ( sp->next->islinear && sp->prev->islinear && psp->next->islinear &&
3819 			psp->me.y == sp->me.y &&
3820 			ptmoves[i].ndir.x*ptmoves[p].pdir.x + ptmoves[i].ndir.y*ptmoves[p].pdir.y<-.999 ) {
3821 		    if ( ptmoves[i].ndir.y<0 )
3822 			sign = -sign;
3823 		    ptmoves[i].newpos.x += sign*zones->stroke_width*ptmoves[i].ndir.x;
3824 		}
3825 	    }
3826 	}
3827 	InterpolateControlPointsAndSet(ptmoves,cnt);
3828     }
3829     free(ptmoves);
3830 
3831     if ( zones->counter_type == ct_retain || (sc->width!=0 && zones->counter_type == ct_auto))
3832 	CorrectLeftSideBearing(ss_expanded,sc,layer);
3833 return(ss_expanded);
3834 }
3835 
LCG_HintedEmboldenHook(SplineSet * ss_expanded,struct lcg_zones * zones,SplineChar * sc,int layer)3836 static SplineSet *LCG_HintedEmboldenHook(SplineSet *ss_expanded,struct lcg_zones *zones,
3837 	SplineChar *sc,int layer) {
3838     SplineSet *ss;
3839     SplinePoint *sp, *nsp, *psp, *origsp;
3840     int cnt, ccnt, i, n, p, j;
3841     struct ptmoves *ptmoves;
3842     StemInfo *h;
3843     double othery;
3844     /* Anything below the baseline moves up by width/2 */
3845     /* Anything that was on the base line moves up so that it still is on the baseline */
3846     /*  If it's a diagonal stem, may want to move in x as well as y */
3847     /* Anything on a hint one side of which is on the base line, then the other */
3848     /*  side moves up so it is hint->width + width above the baseline */
3849 
3850     /* Same for either x-height or cap-height, except that everything moves down */
3851 
3852     /* Any points on hints between baseline/x-height are fixed */
3853 
3854     /* Other points between baseline/x-height follow IUP rules */
3855 
3856     if ( layer!=ly_fore || sc->hstem==NULL )
3857 return( LCG_EmboldenHook(ss_expanded,zones,sc,layer));
3858 
3859     FindStartPoint(ss_expanded,sc,layer);
3860     ccnt = MaxContourCount(ss_expanded);
3861     if ( ccnt==0 )
3862 return(ss_expanded);			/* No points? Nothing to do */
3863     ptmoves = malloc((ccnt+1)*sizeof(struct ptmoves));
3864     for ( ss = ss_expanded; ss!=NULL ; ss=ss->next ) {
3865 	if ( ss->first->prev==NULL )
3866     continue;
3867 	cnt = PtMovesInitToContour(ptmoves,ss);
3868 	/* Touch (usually move) all points which are either in our zones or on a hint */
3869 	for ( i=0; i<cnt; ++i ) {
3870 	    int sign = 0;
3871 	    sp = ptmoves[i].sp;
3872 	    origsp = FindMatchingPoint(sp->ptindex,sc->layers[layer].splines);
3873 	    h = NULL; othery = 0;
3874 	    if ( origsp!=NULL )
3875 		h = OnHint(sc->hstem,origsp->me.y,&othery);
3876 	    else
3877 		h = MightBeOnHint(sc->hstem,zones,&ptmoves[i],&othery);
3878 
3879 	    /* Fix vertical serifs */
3880 	    if ( zones->serif_height>0 &&
3881 		    (( origsp!=NULL && RealWithin(origsp->me.y, zones->bottom_bound+zones->serif_height,zones->serif_fuzz)) ||
3882 		     RealWithin(sp->me.y,zones->bottom_bound+zones->serif_height+zones->stroke_width/2,zones->serif_fuzz)) ) {
3883 		ptmoves[i].touched = true;
3884 		ptmoves[i].newpos.y = zones->bottom_bound+zones->serif_height;
3885 	    } else if ( zones->serif_height>0 &&
3886 		    (( origsp!=NULL && RealWithin(origsp->me.y, zones->top_bound-zones->serif_height,zones->serif_fuzz)) ||
3887 		     RealWithin(sp->me.y,zones->top_bound-zones->serif_height-zones->stroke_width/2,zones->serif_fuzz)) ) {
3888 		ptmoves[i].touched = true;
3889 		ptmoves[i].newpos.y = zones->top_bound-zones->serif_height;
3890 	    } else if ( sp->me.y>=zones->top_bound || (h!=NULL && othery+zones->stroke_width/2>=zones->top_bound))
3891 		sign = -1;
3892 	    else if ( sp->me.y<=zones->bottom_bound || (h!=NULL && othery-zones->stroke_width/2<=zones->bottom_bound))
3893 		sign = 1;
3894 	    else if ( origsp!=NULL &&
3895 			    (InHintAroundZone(sc->hstem,origsp->me.y,zones->top_bound) ||
3896 			     InHintAroundZone(sc->hstem,origsp->me.y,zones->bottom_bound)) )
3897 		/* It's not on the hint, so we want to interpolate it even if */
3898 		/*  it's in an area we'd normally move (tahoma "s" has two */
3899 		/*  points which fall on the baseline but aren't on the bottom */
3900 		/*  hint */
3901 		/* Do Nothing */;
3902 	    else if ( h!=NULL &&
3903 		    ((h->start>=zones->bottom_zone && h->start<=zones->top_zone) ||
3904 		     (h->start+h->width>=zones->bottom_zone && h->start+h->width<=zones->top_zone)) ) {
3905 		/* Point on a hint. In the middle of the glyph */
3906 		/*  This one not in the zones, so it is fixed */
3907 		ptmoves[i].touched = true;
3908 	    }
3909 	    if ( sign ) {
3910 		ptmoves[i].touched = true;
3911 		p = i==0?cnt-1:i-1;
3912 		nsp = ptmoves[i+1].sp;
3913 		psp = ptmoves[p].sp;
3914 		if ( origsp!=NULL && ((origsp->me.y>=zones->bottom_bound-2 && origsp->me.y<=zones->bottom_bound+2 ) ||
3915 			(origsp->me.y>=zones->top_bound-2 && origsp->me.y<=zones->top_bound+2 )) ) {
3916 		    ptmoves[i].newpos.y += sign*zones->stroke_width*ptmoves[i].factor/2;
3917 		    /* This is to improve the looks of diagonal stems */
3918 		    if ( sp->next->islinear && sp->prev->islinear && nsp->next->islinear &&
3919 			    nsp->me.y == sp->me.y &&
3920 			    ptmoves[i].pdir.x*ptmoves[i+1].ndir.x + ptmoves[i].pdir.y*ptmoves[i+1].ndir.y<-.999 ) {
3921 			if ( ptmoves[i].pdir.y<0 )
3922 			    sign = -sign;
3923 			ptmoves[i].newpos.x += sign*zones->stroke_width*ptmoves[i].pdir.x;
3924 		    } else if ( sp->next->islinear && sp->prev->islinear && psp->next->islinear &&
3925 			    psp->me.y == sp->me.y &&
3926 			    ptmoves[i].ndir.x*ptmoves[p].pdir.x + ptmoves[i].ndir.y*ptmoves[p].pdir.y<-.999 ) {
3927 			if ( ptmoves[i].ndir.y<0 )
3928 			    sign = -sign;
3929 			ptmoves[i].newpos.x += sign*zones->stroke_width*ptmoves[i].ndir.x;
3930 		    }
3931 		} else
3932 		    ptmoves[i].newpos.y += sign*zones->stroke_width/2;
3933 	    }
3934 	}
3935 	/* Now find each untouched point and interpolate how it moves */
3936 	for ( i=0; i<cnt; ++i ) if ( !ptmoves[i].touched ) {
3937 	    for ( p=i-1; p!=i; --p ) {
3938 		if ( p<0 ) {
3939 		    p=cnt-1;
3940 		    if ( p==i )
3941 	    break;
3942 		}
3943 		if ( ptmoves[p].touched )
3944 	    break;
3945 	    }
3946 	    if ( p==i )
3947 	break;			/* Nothing on the contour touched. Can't change anything */
3948 	    for ( n=i+1; n!=i; ++n ) {
3949 		if ( n>=cnt ) {
3950 		    n=0;
3951 		    if ( n==i )
3952 	    break;
3953 		}
3954 		if ( ptmoves[n].touched )
3955 	    break;
3956 	    }
3957 	    nsp = ptmoves[n].sp;
3958 	    psp = ptmoves[p].sp;
3959 	    for ( j=p+1; j!=n; ++j ) {
3960 		if ( j==cnt ) {
3961 		    j=0;
3962 		    if ( n==0 )
3963 	    break;
3964 		}
3965 		sp = ptmoves[j].sp;
3966 		if (( sp->me.y>nsp->me.y && sp->me.y>psp->me.y ) ||
3967 			(sp->me.y<nsp->me.y && sp->me.y<psp->me.y )) {
3968 		    if (( sp->me.y>nsp->me.y && nsp->me.y>psp->me.y ) ||
3969 			    (sp->me.y<nsp->me.y && nsp->me.y<psp->me.y )) {
3970 			ptmoves[j].newpos.y += ptmoves[n].newpos.y-nsp->me.y;
3971 			ptmoves[j].newpos.x += ptmoves[n].newpos.x-nsp->me.x ;
3972 		    } else {
3973 			ptmoves[j].newpos.y += ptmoves[p].newpos.y-psp->me.y;
3974 			ptmoves[j].newpos.x += ptmoves[p].newpos.x-psp->me.x ;
3975 		    }
3976 		} else {
3977 		    double diff;
3978 		    diff = nsp->me.y - psp->me.y;
3979 		    if ( diff!=0 ) {
3980 			ptmoves[j].newpos.y += (ptmoves[p].newpos.y-psp->me.y)
3981 						+ (sp->me.y-psp->me.y)*(ptmoves[n].newpos.y-nsp->me.y-(ptmoves[p].newpos.y-psp->me.y))/diff;
3982 			/* Note we even interpolate the x direction depending on */
3983 			/*  y position */
3984 			ptmoves[j].newpos.x += (ptmoves[p].newpos.x-psp->me.x)
3985 						+ (sp->me.y-psp->me.y)*(ptmoves[n].newpos.x-nsp->me.x-(ptmoves[p].newpos.x-psp->me.x))/diff;
3986 		    } else if ( (diff = nsp->me.x - psp->me.x)!=0 ) {
3987 			ptmoves[j].newpos.x += (ptmoves[p].newpos.x-psp->me.x)
3988 						+ (sp->me.x-psp->me.x)*(ptmoves[n].newpos.x-nsp->me.x-(ptmoves[p].newpos.x-psp->me.x))/diff;
3989 			/* Note we even interpolate the y direction depending on */
3990 			/*  x position */
3991 			ptmoves[j].newpos.y += (ptmoves[p].newpos.y-psp->me.y)
3992 						+ (sp->me.x-psp->me.x)*(ptmoves[n].newpos.y-nsp->me.y-(ptmoves[p].newpos.y-psp->me.y))/diff;
3993 		    }
3994 		}
3995 		ptmoves[j].touched = true;
3996 		if ( isnan( ptmoves[j].newpos.y ))
3997 		    IError("Nan value in LCG_HintedEmboldenHook\n" );
3998 	    }
3999 	}
4000 	InterpolateControlPointsAndSet(ptmoves,cnt);
4001     }
4002     free(ptmoves);
4003 
4004     if ( zones->counter_type == ct_retain || (sc->width!=0 && zones->counter_type == ct_auto))
4005 	CorrectLeftSideBearing(ss_expanded,sc,layer);
4006 return( ss_expanded );
4007 }
4008 
AdjustCounters(SplineChar * sc,struct lcg_zones * zones,DBounds * old,DBounds * new)4009 static void AdjustCounters(SplineChar *sc, struct lcg_zones *zones,
4010 	DBounds *old, DBounds *new) {
4011     struct counterinfo ci;
4012 
4013     /* I did the left side bearing as I went along. I'll do the right side */
4014     /*  bearing now. I don't use condense/extend because I have more info */
4015     /*  here, and because I might not want to adjust both by the same amount */
4016     SCSynchronizeWidth(sc,sc->width+rint(zones->stroke_width),sc->width,NULL);
4017     /* Now do the internal counters. The Emboldening will (for vertical stems)*/
4018     /*  have made counters smaller by stroke_width (diagonal stems who knows) */
4019     /*  so make them bigger by that amount */
4020     memset(&ci,0,sizeof(ci));
4021     ci.bd = zones->bd;
4022     ci.stdvw = zones->stdvw;
4023     ci.top_y = zones->top_bound;
4024     ci.bottom_y = zones->bottom_bound;
4025     ci.boundry = (zones->top_bound+zones->bottom_bound)/2;
4026     ci.c_add = zones->stroke_width;
4027     ci.c_factor = ci.sb_factor = 100;
4028     StemInfosFree(sc->vstem); sc->vstem = NULL;
4029     SCCondenseExtend(&ci,sc,ly_fore,false);
4030 }
4031 
SCEmbolden(SplineChar * sc,struct lcg_zones * zones,int layer)4032 static void SCEmbolden(SplineChar *sc, struct lcg_zones *zones, int layer) {
4033     StrokeInfo si;
4034     SplineSet *temp;
4035     DBounds old, new;
4036     int adjust_counters;
4037 
4038     InitializeStrokeInfo(&si);
4039     si.stroke_type = si_round;
4040     SITranslatePSArgs(&si, lj_miter, lc_square);
4041     si.rmov = srmov_contour;
4042     if ( zones->stroke_width>=0 ) {
4043 	si.width = zones->stroke_width;
4044 	si.removeinternal = true;
4045     } else {
4046 	si.width = -zones->stroke_width;
4047 	si.removeexternal = true;
4048     }
4049 
4050     if ( layer!=ly_back && zones->wants_hints &&
4051 	    sc->hstem == NULL && sc->vstem==NULL && sc->dstem==NULL ) {
4052 	_SplineCharAutoHint(sc,layer==ly_all?ly_fore:layer,&zones->bd,NULL,false);
4053     }
4054 
4055     adjust_counters = zones->counter_type==ct_retain ||
4056 	    (zones->counter_type==ct_auto &&
4057 		zones->embolden_hook==LCG_HintedEmboldenHook &&
4058 		sc->width>0 );
4059 
4060     if ( layer==ly_all ) {
4061 	SCPreserveState(sc,false);
4062 	SplineCharFindBounds(sc,&old);
4063 	for ( layer = ly_fore; layer<sc->layer_cnt; ++layer ) {
4064 	    NumberLayerPoints(sc->layers[layer].splines);
4065 	    temp = BoldSSStroke(sc->layers[layer].splines,&si,sc->layers[layer].order2,zones->removeoverlap);
4066 	    if ( zones->embolden_hook!=NULL )
4067 		temp = (zones->embolden_hook)(temp,zones,sc,layer);
4068 	    SplinePointListsFree( sc->layers[layer].splines );
4069 	    sc->layers[layer].splines = temp;
4070 	}
4071 	SplineCharFindBounds(sc,&new);
4072 	if ( adjust_counters )
4073 	    AdjustCounters(sc,zones,&old,&new);
4074 	layer = ly_all;
4075     } else if ( layer>=0 ) {
4076 	SCPreserveLayer(sc,layer,false);
4077 	NumberLayerPoints(sc->layers[layer].splines);
4078 	SplineSetFindBounds(sc->layers[layer].splines,&old);
4079 	temp = BoldSSStroke(sc->layers[layer].splines,&si,sc->layers[layer].order2,zones->removeoverlap);
4080 	if ( zones->embolden_hook!=NULL )
4081 	    temp = (zones->embolden_hook)(temp,zones,sc,layer);
4082 	SplineSetFindBounds(temp,&new);
4083 	SplinePointListsFree( sc->layers[layer].splines );
4084 	sc->layers[layer].splines = temp;
4085 	if ( adjust_counters && layer==ly_fore )
4086 	    AdjustCounters(sc,zones,&old,&new);
4087     }
4088 
4089     if ( layer!=ly_back ) {
4090 	/* Hints will be inccorrect (misleading) after these transformations */
4091 	StemInfosFree(sc->vstem); sc->vstem=NULL;
4092 	StemInfosFree(sc->hstem); sc->hstem=NULL;
4093 	DStemInfosFree(sc->dstem); sc->dstem=NULL;
4094 	SCOutOfDateBackground(sc);
4095     }
4096     SCCharChangedUpdate(sc,layer);
4097 }
4098 
4099 static struct {
4100     uint32 script;
4101     SplineSet *(*embolden_hook)(SplineSet *,struct lcg_zones *,SplineChar *, int layer);
4102 } script_hooks[] = {
4103     { CHR('l','a','t','n'), LCG_HintedEmboldenHook },
4104     { CHR('c','y','r','l'), LCG_HintedEmboldenHook },
4105     { CHR('g','r','e','k'), LCG_HintedEmboldenHook },
4106 	/* Hebrew probably works too */
4107     { CHR('h','e','b','r'), LCG_HintedEmboldenHook },
4108     { 0, NULL }
4109 };
4110 
4111 static struct {
4112     unichar_t from, to;
4113     SplineSet *(*embolden_hook)(SplineSet *,struct lcg_zones *,SplineChar *, int layer);
4114 } char_hooks[] = {
4115     { '0','9', LCG_HintedEmboldenHook },
4116     { '$','%', LCG_HintedEmboldenHook },
4117     { '\0', '\0', NULL }
4118 };
4119 
LCG_ZoneInit(SplineFont * sf,int layer,struct lcg_zones * zones,enum embolden_type type)4120 static void LCG_ZoneInit(SplineFont *sf, int layer, struct lcg_zones *zones,enum embolden_type type) {
4121 
4122     if ( type == embolden_lcg || type == embolden_custom) {
4123 	zones->embolden_hook = LCG_HintedEmboldenHook;
4124     } else {
4125 	zones->embolden_hook = NULL;
4126     }
4127     QuickBlues(sf, layer, &zones->bd);
4128     zones->stdvw = SFStdVW(sf);
4129 }
4130 
BlueSearch(char * bluestring,double value,double bestvalue)4131 static double BlueSearch(char *bluestring, double value, double bestvalue) {
4132     char *end;
4133     double try, diff, bestdiff;
4134 
4135     if ( *bluestring=='[' ) ++bluestring;
4136     if ( (bestdiff = bestvalue-value)<0 ) bestdiff = -bestdiff;
4137 
4138     for (;;) {
4139 	try = strtod(bluestring,&end);
4140 	if ( bluestring==end )
4141 return( bestvalue );
4142 	if ( (diff = try-value)<0 ) diff = -diff;
4143 	if ( diff<bestdiff ) {
4144 	    bestdiff = diff;
4145 	    bestvalue = try;
4146 	}
4147 	bluestring = end;
4148 	(void) strtod(bluestring,&end);		/* Skip the top of blue zone value */
4149 	bluestring = end;
4150     }
4151 }
4152 
SearchBlues(SplineFont * sf,int type,double value)4153 static double SearchBlues(SplineFont *sf,int type,double value) {
4154     char *blues, *others;
4155     double bestvalue;
4156 
4157     if ( type=='x' )
4158 	value = sf->ascent/2;		/* Guess that the x-height is about half the ascent and then see what we find */
4159     if ( type=='I' )
4160 	value = 4*sf->ascent/5;		/* Guess that the cap-height is 4/5 the ascent */
4161 
4162     blues = others = NULL;
4163     if ( sf->private!=NULL ) {
4164 	blues = PSDictHasEntry(sf->private,"BlueValues");
4165 	others = PSDictHasEntry(sf->private,"OtherBlues");
4166     }
4167     bestvalue = 0x100000;		/* Random number outside coord range */
4168     if ( blues!=NULL )
4169 	bestvalue = BlueSearch(blues,value,bestvalue);
4170     if ( others!=NULL )
4171 	bestvalue = BlueSearch(others,value,bestvalue);
4172     if ( bestvalue == 0x100000 )
4173 return( value );
4174 
4175 return( bestvalue );
4176 }
4177 
SFSerifHeight(SplineFont * sf)4178 double SFSerifHeight(SplineFont *sf) {
4179     SplineChar *isc;
4180     SplineSet *ss;
4181     SplinePoint *sp;
4182     DBounds b;
4183 
4184     if ( sf->strokedfont || sf->multilayer )
4185 return( 0 );
4186 
4187     isc = SFGetChar(sf,'I',NULL);
4188     if ( isc==NULL )
4189 	isc = SFGetChar(sf,0x0399,"Iota");
4190     if ( isc==NULL )
4191 	isc = SFGetChar(sf,0x0406,NULL);
4192     if ( isc==NULL )
4193 return( 0 );
4194 
4195     ss = isc->layers[ly_fore].splines;
4196     if ( ss==NULL || ss->next!=NULL )		/* Too complicated, probably doesn't have simple serifs (black letter?) */
4197 return( 0 );
4198     if ( ss->first->prev==NULL )
4199 return( 0 );
4200     for ( sp=ss->first; ; ) {
4201 	if ( sp->me.y==0 )
4202     break;
4203 	sp = sp->next->to;
4204 	if ( sp==ss->first )
4205     break;
4206     }
4207     if ( sp->me.y!=0 )
4208 return( 0 );
4209     SplineCharFindBounds(isc,&b);
4210     if ( sp->next->to->me.y==0 || sp->next->to->next->to->me.y==0 ) {
4211 	SplinePoint *psp = sp->prev->from;
4212 	if ( psp->me.y>=b.maxy/3 )
4213 return( 0 );			/* Sans Serif, probably */
4214 	if ( !psp->nonextcp && psp->nextcp.x==psp->me.x ) {
4215 	    /* A curve point half-way up the serif? */
4216 	    psp = psp->prev->from;
4217 	    if ( psp->me.y>=b.maxy/3 )
4218 return( 0 );			/* I give up, I don't understand this */
4219 	}
4220 return( psp->me.y );
4221     } else if ( sp->prev->from->me.y==0 || sp->prev->from->prev->from->me.y==0 ) {
4222 	SplinePoint *nsp = sp->next->to;
4223 	if ( nsp->me.y>=b.maxy/3 )
4224 return( 0 );			/* Sans Serif, probably */
4225 	if ( !nsp->nonextcp && nsp->nextcp.x==nsp->me.x ) {
4226 	    /* A curve point half-way up the serif? */
4227 	    nsp = nsp->next->to;
4228 	    if ( nsp->me.y>=b.maxy/3 )
4229 return( 0 );			/* I give up, I don't understand this */
4230 	}
4231 return( nsp->me.y );
4232     }
4233 
4234     /* Too complex for me */
4235 return( 0 );
4236 }
4237 
PerGlyphInit(SplineChar * sc,struct lcg_zones * zones,enum embolden_type type)4238 static void PerGlyphInit(SplineChar *sc, struct lcg_zones *zones,
4239 	enum embolden_type type) {
4240     int j;
4241     SplineChar *hebrew;
4242 
4243     if ( type == embolden_auto ) {
4244 	zones->embolden_hook = NULL;
4245 	for ( j=0; char_hooks[j].from!=0; ++j ) {
4246 	    if ( sc->unicodeenc>=char_hooks[j].from && sc->unicodeenc<=char_hooks[j].to ) {
4247 		zones->embolden_hook = char_hooks[j].embolden_hook;
4248 	break;
4249 	    }
4250 	}
4251 	if ( zones->embolden_hook == NULL ) {
4252 	    uint32 script = SCScriptFromUnicode(sc);
4253 	    for ( j=0; script_hooks[j].script!=0; ++j ) {
4254 		if ( script==script_hooks[j].script ) {
4255 		    zones->embolden_hook = script_hooks[j].embolden_hook;
4256 	    break;
4257 		}
4258 	    }
4259 	}
4260     }
4261     if ( type == embolden_lcg || type == embolden_auto ) {
4262 	zones->bottom_bound = 0;
4263 	if ( SCScriptFromUnicode(sc)==CHR('h','e','b','r') &&
4264 		(hebrew=SFGetChar(sc->parent,0x05df,NULL))!=NULL ) {
4265 	    DBounds b;
4266 	    SplineCharFindBounds(hebrew,&b);
4267 	    zones->bottom_zone = b.maxy/3;
4268 	    zones->top_zone = 2*b.maxy/3;
4269 	    zones->top_bound = b.maxy;
4270 	} else if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && islower(sc->unicodeenc)) {
4271 	    if ( zones->bd.xheight<=0 )
4272 		zones->bd.xheight = SearchBlues(sc->parent,'x',0);
4273 	    zones->bottom_zone = zones->bd.xheight>0 ? zones->bd.xheight/3 :
4274 			    zones->bd.caph>0 ? zones->bd.caph/3 :
4275 			    (sc->parent->ascent/4);
4276 	    zones->top_zone = zones->bd.xheight>0 ? 2*zones->bd.xheight/3 :
4277 			    zones->bd.caph>0 ? zones->bd.caph/2 :
4278 			    (sc->parent->ascent/3);
4279 	    zones->top_bound = zones->bd.xheight>0 ? zones->bd.xheight :
4280 			    zones->bd.caph>0 ? 2*zones->bd.caph/3 :
4281 			    (sc->parent->ascent/2);
4282 	} else if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && isupper(sc->unicodeenc)) {
4283 	    if ( zones->bd.caph<0 )
4284 		zones->bd.caph = SearchBlues(sc->parent,'I',0);
4285 	    zones->bottom_zone = zones->bd.caph>0 ? zones->bd.caph/3 :
4286 			    (sc->parent->ascent/4);
4287 	    zones->top_zone = zones->bd.caph>0 ? 2*zones->bd.caph/3 :
4288 			    (sc->parent->ascent/2);
4289 	    zones->top_bound = zones->bd.caph>0?zones->bd.caph:4*sc->parent->ascent/5;
4290 	} else {
4291 	    /* It's not upper case. It's not lower case. Hmm. Look for blue */
4292 	    /*  values near the top and bottom of the glyph */
4293 	    DBounds b;
4294 
4295 	    SplineCharFindBounds(sc,&b);
4296 	    zones->top_bound = SearchBlues(sc->parent,0,b.maxy);
4297 	    zones->bottom_bound = SearchBlues(sc->parent,-1,b.miny);
4298 	    zones->top_zone = zones->bottom_bound + 3*(zones->top_bound-zones->bottom_bound)/4;
4299 	    zones->bottom_zone = zones->bottom_bound + (zones->top_bound-zones->bottom_bound)/4;
4300 	}
4301     }
4302     zones->wants_hints = zones->embolden_hook == LCG_HintedEmboldenHook;
4303 }
4304 
FVEmbolden(FontViewBase * fv,enum embolden_type type,struct lcg_zones * zones)4305 void FVEmbolden(FontViewBase *fv,enum embolden_type type,struct lcg_zones *zones) {
4306     int i, gid, cnt;
4307     SplineChar *sc;
4308 
4309     LCG_ZoneInit(fv->sf,fv->active_layer,zones,type);
4310 
4311     for (i=0, cnt=0; i < fv->map->enccount; ++i) {
4312         if (fv->selected[i] && (gid = fv->map->map[i]) != -1 &&
4313             (sc=fv->sf->glyphs[gid]) != NULL) {
4314 
4315             cnt++;
4316         }
4317     }
4318 
4319     ff_progress_start_indicator(10, _("Change Weight"),
4320         _("Changing glyph weights"), NULL, cnt, 1);
4321 
4322     for ( i=0; i<fv->map->enccount; ++i ) if ( fv->selected[i] &&
4323 	    (gid = fv->map->map[i])!=-1 && (sc=fv->sf->glyphs[gid])!=NULL ) {
4324 	PerGlyphInit(sc,zones,type);
4325 	SCEmbolden(sc, zones, -2);		/* -2 => all foreground layers */
4326     if (!ff_progress_next()) {
4327         break;
4328     }
4329     }
4330     ff_progress_end_indicator();
4331 }
4332 
CVEmbolden(CharViewBase * cv,enum embolden_type type,struct lcg_zones * zones)4333 void CVEmbolden(CharViewBase *cv,enum embolden_type type,struct lcg_zones *zones) {
4334     SplineChar *sc = cv->sc;
4335 
4336     if ( cv->drawmode == dm_grid )
4337 return;
4338 
4339     LCG_ZoneInit(sc->parent,CVLayer(cv),zones,type);
4340 
4341     PerGlyphInit(sc,zones,type);
4342     SCEmbolden(sc, zones, CVLayer(cv));
4343 }
4344 
ScriptSCEmbolden(SplineChar * sc,int layer,enum embolden_type type,struct lcg_zones * zones)4345 void ScriptSCEmbolden(SplineChar *sc,int layer,enum embolden_type type,struct lcg_zones *zones) {
4346 
4347     LCG_ZoneInit(sc->parent,layer,zones,type);
4348 
4349     PerGlyphInit(sc,zones,type);
4350     SCEmbolden(sc, zones, layer);
4351 }
4352 
4353 /* ************************************************************************** */
4354 /* ********************************* Italic ********************************* */
4355 /* ************************************************************************** */
4356 
FigureGoodStems(StemInfo * hints)4357 static void FigureGoodStems(StemInfo *hints) {
4358     StemInfo *h, *test, *best;
4359     double max, best_len;
4360 
4361     for ( h=hints; h!=NULL; ) {
4362 	h->tobeused = false;
4363 	if ( 2*HIlen(h)<h->width ) {
4364 	    h = h->next;
4365     continue;
4366 	}
4367 	if ( !h->hasconflicts ) {
4368 	    h->tobeused = true;
4369 	    h = h->next;
4370 	} else {
4371 	    max = h->start+h->width;
4372 	    best = h;
4373 	    best_len = HIlen(h);
4374 	    for ( test=h->next; test!=NULL; test=test->next ) {
4375 		if ( test->start>max )
4376 	    break;
4377 		if ( test->start+test->width>max )
4378 		    max = test->start + test->width;
4379 		if ( HIlen(test)>best_len ) {
4380 		    best = test;
4381 		    best_len = HIlen(test);
4382 		}
4383 	    }
4384 	    best->tobeused = true;
4385 	    h = test;
4386 	}
4387     }
4388 }
4389 
4390 enum { cs_lc, cs_uc, cs_smallcaps, cs_neither };
FigureCase(SplineChar * sc)4391 static int FigureCase(SplineChar *sc) {
4392     char *under, *dot, ch;
4393     int uni;
4394     int smallcaps = false;
4395 
4396     if ( sc->unicodeenc<0x10000 && sc->unicodeenc!=-1 )
4397 return( islower(sc->unicodeenc) ? cs_lc : isupper(sc->unicodeenc) ? cs_uc : cs_neither );
4398     else if ( sc->unicodeenc!=-1 )
4399 return( cs_neither );
4400 
4401     under = strchr(sc->name,'_');
4402     dot   = strchr(sc->name,'.');
4403     if ( dot!=NULL )
4404 	smallcaps = strcmp(dot,".sc")==0 || strcmp(dot,".small")==0;
4405     if ( under!=NULL && (dot==NULL || dot>under))
4406 	dot = under;
4407     if ( dot==NULL )
4408 return( cs_neither );
4409     ch = *dot; *dot = '\0';
4410     uni = UniFromName(sc->name,ui_none,&custom);
4411     *dot = ch;
4412     if ( uni==-1 || uni>=0x10000 )
4413 return( cs_neither );
4414 
4415     if ( smallcaps && (islower(uni) || isupper(uni)) )
4416 return( cs_smallcaps );
4417 
4418 return( islower(uni) ? cs_lc : isupper(uni) ? cs_uc : cs_neither );
4419 }
4420 
LikeAnF(SplineChar * sc)4421 static int LikeAnF(SplineChar *sc) {
4422     char *under, *start;
4423     int cnt;
4424 
4425     if ( sc->unicodeenc=='f' || sc->unicodeenc==0x17f/*longs*/ ||
4426 	    sc->unicodeenc==0xfb /* longs-s ligature (es-zet) */ ||
4427 	    sc->unicodeenc==0xfb01 || sc->unicodeenc==0xfb02 ||	/* fi, fl */
4428 	    sc->unicodeenc==0xfb05 /*longs_t */ )
4429 return( true );
4430     if ( sc->unicodeenc==0xfb00 || sc->unicodeenc==0xfb03 || sc->unicodeenc==0xfb04 )
4431 return( 2 );	/* ff, ffi, ffl */
4432 
4433     cnt = 0;
4434     for ( start = sc->name; (under= strchr(start,'_'))!=NULL; ) {
4435 	if ( *start=='f' && under-start==1 )
4436 	    ++cnt;		/* f ligature */
4437 	else if ( under-start==5 && strncmp(start,"longs",5)==0 )
4438 	    ++cnt;		/* longs ligature */
4439 	else
4440 return( cnt );
4441 	start = under+1;
4442     }
4443     if ( *start=='f' && start[1]=='\0' )
4444 	++cnt;
4445     else if ( strcmp(start,"longs")==0 )
4446 	++cnt;
4447 return( cnt );
4448 }
4449 
LikePQ(SplineChar * sc)4450 static int LikePQ(SplineChar *sc) {
4451     int i;
4452 
4453     for ( i=0; descender_str[i]!=0; ++i )
4454 	if ( sc->unicodeenc == descender_str[i] )
4455 return( true );
4456 
4457 return( false );
4458 }
4459 
ItalicCompress(SplineChar * sc,int layer,struct hsquash * squash)4460 static void ItalicCompress(SplineChar *sc,int layer,struct hsquash *squash) {
4461     DBounds pre, mid, post;
4462     real stemsquash[6], translate[6];
4463     double counter_len;
4464     int tot;
4465 
4466     SplineCharLayerFindBounds(sc,layer,&pre);
4467     if ( squash->stem_percent!=1 ) {
4468 	memset(stemsquash,0,sizeof(stemsquash));
4469 	stemsquash[0] = squash->stem_percent;
4470 	stemsquash[3] = 1;
4471 	SplinePointListTransform(sc->layers[layer].splines,stemsquash,tpt_AllPoints);
4472     }
4473     if ( !RealNear(squash->stem_percent,squash->counter_percent)) {
4474 	SplineCharAutoHint(sc,layer,NULL);
4475 
4476 	free( SCFindHintOverlaps(sc->vstem, pre.minx*squash->stem_percent,
4477 		pre.maxx*squash->stem_percent, &tot, &counter_len ));
4478 	if ( counter_len>0 ) {
4479 	    SplineCharLayerFindBounds(sc,layer,&mid);
4480 	    SmallCapsRemoveSpace(sc->layers[layer].splines,sc->anchor,sc->vstem,0,
4481 		    counter_len*(1-squash->counter_percent/squash->stem_percent),
4482 		    mid.minx,mid.maxx);
4483 	    SplineSetRefigure(sc->layers[layer].splines);
4484 	}
4485     }
4486     if ( sc->layers[layer].refs==NULL ) {
4487 	SplineCharLayerFindBounds(sc,layer,&post);
4488 	memset(translate,0,sizeof(translate));
4489 	if ( (pre.minx==0 && post.minx!=0 ) ||
4490 		(pre.minx!=0 && !RealNear(post.minx/pre.minx,squash->lsb_percent))) {
4491 	    translate[0] = translate[3] = 1;
4492 	    translate[4] = pre.minx==0 ? -post.minx : pre.minx*squash->lsb_percent - post.minx;
4493 	    SplinePointListTransform(sc->layers[layer].splines,translate,tpt_AllPoints);
4494 	}
4495 	sc->width = (sc->width-pre.maxx)*squash->rsb_percent + post.maxx + translate[4];
4496     }
4497 }
4498 
4499 enum pt_type { pt_oncurve, pt_offcurve, pt_end };
4500 
4501 struct italicserifdata {
4502     int emsize;
4503     double stemwidth;
4504     double xheight;
4505     double ia;
4506     double stem_angle;
4507     struct { double x,y; enum pt_type type; } points[27];
4508 };
4509 static struct italicserifdata normalitalicserif =	/* faces right */
4510     { 1000, 84, 450, -15.2, 90, /* vertical */{
4511 	{ 84,     116, pt_oncurve },
4512 	{ 84,     97,  pt_offcurve },
4513 	{ 85.19,  61,  pt_offcurve },
4514 	{ 86.07,  51,  pt_oncurve },
4515 	{ 88.09,  44,  pt_offcurve },
4516 	{ 98.86,  36,  pt_offcurve },
4517 	{ 107.34, 36,  pt_oncurve },
4518 	{ 116.87, 36,  pt_offcurve },
4519 	{ 133.42, 46,  pt_offcurve },
4520 	{ 143,    63,  pt_oncurve },
4521 	{ 171.75, 114, pt_oncurve },
4522 	{ 188.68, 103, pt_oncurve },
4523 	{ 152.48, 19,  pt_offcurve },
4524 	{ 121.92, -11, pt_offcurve },
4525 	{ 73.20,  -11, pt_oncurve },
4526 	{ 14.64,  -11, pt_offcurve },
4527 	{ 0,      47,  pt_offcurve },
4528 	{ 0,      123, pt_oncurve },
4529 	{ 0, 0, pt_end }} };
4530 static struct italicserifdata bolditalicserif =
4531     { 1000, 139, 450, -15.2, 90, /* vertical */{
4532 	{ 139,    128, pt_oncurve },
4533 	{ 139,    116, pt_offcurve },
4534 	{ 143.21, 82,  pt_offcurve },
4535 	{ 145.17, 76,  pt_oncurve },
4536 	{ 147.78, 68,  pt_offcurve },
4537 	{ 158.48, 61,  pt_offcurve },
4538 	{ 168.09, 61,  pt_oncurve },
4539 	{ 186.12, 61,  pt_offcurve },
4540 	{ 205.36, 82,  pt_offcurve },
4541 	{ 225.43, 121, pt_oncurve },
4542 	{ 235.72, 141, pt_oncurve },
4543 	{ 266.74, 127, pt_oncurve },
4544 	{ 216.89, 22,  pt_offcurve },
4545 	{ 182.54, -9,  pt_offcurve },
4546 	{ 110.42, -9,  pt_oncurve },
4547 	{ 0.74,   -9,  pt_offcurve },
4548 	{ 0,      80,  pt_offcurve },
4549 	{ 0,      123, pt_oncurve },
4550 	{ 0, 0, pt_end }} };
4551 static struct italicserifdata leftfacingitalicserif =	/* bottom left of "x", and top right */
4552     { 1000, 34, 450, -15.2, 90, /* vertical */{
4553 	{   25,    111, pt_oncurve },
4554 	{   25,    91, pt_offcurve },
4555 	{   19,     75, pt_offcurve },
4556 	{   10,     59, pt_offcurve },
4557 	{  -24,      1, pt_offcurve },
4558 	{  -33,    -11, pt_offcurve },
4559 	{  -64,    -11, pt_oncurve },
4560 	{  -94,    -11, pt_offcurve },
4561 	{ -118,      5, pt_offcurve },
4562 	{ -125,     31, pt_oncurve },
4563 	{ -130,     51, pt_offcurve },
4564 	{ -120,     66, pt_offcurve },
4565 	{ -101,     66, pt_oncurve },
4566 	{  -93,     66, pt_offcurve },
4567 	{  -81,     63, pt_offcurve },
4568 	{  -66,     56, pt_oncurve },
4569 	{  -54,     50, pt_offcurve },
4570 	{  -43,     47, pt_offcurve },
4571 	{  -37,     47, pt_oncurve },
4572 	{  -28,     47, pt_offcurve },
4573 	{  -18,     56, pt_offcurve },
4574 	{   -8,     76, pt_oncurve },
4575 	{   -2,     89, pt_offcurve },
4576 	{    0,     98, pt_offcurve },
4577 	{    0,    111, pt_oncurve },
4578 	{ 0, 0, pt_end }} };
4579 static struct italicserifdata boldleftfacingitalicserif =	/* bottom left of "x", and top right */
4580     { 1000, 60, 450, -15.2, 90, /* vertical */{
4581 	{   39,    176, pt_oncurve },
4582 	{   39,    159, pt_offcurve },
4583 	{   33,    131, pt_offcurve },
4584 	{   24,    103, pt_offcurve },
4585 	{   -8,      7, pt_offcurve },
4586 	{  -25,    -13, pt_offcurve },
4587 	{  -75,    -13, pt_oncurve },
4588 	{ -113,    -13, pt_offcurve },
4589 	{ -146,     11, pt_offcurve },
4590 	{ -156,     46, pt_oncurve },
4591 	{ -164,     77, pt_offcurve },
4592 	{ -147,    102, pt_offcurve },
4593 	{ -116,    102, pt_oncurve },
4594 	{ -104,    102, pt_offcurve },
4595 	{  -91,     98, pt_offcurve },
4596 	{  -72,     89, pt_oncurve },
4597 	{  -61,     83, pt_offcurve },
4598 	{  -55,     81, pt_offcurve },
4599 	{  -49,     81, pt_oncurve },
4600 	{  -32,     81, pt_offcurve },
4601 	{  -23,     90, pt_offcurve },
4602 	{  -12,    122, pt_oncurve },
4603 	{   -4,    144, pt_offcurve },
4604 	{    0,    162, pt_offcurve },
4605 	{    0,    180, pt_oncurve },
4606 	{ 0, 0, pt_end }} };
4607 static struct italicserifdata *normalserifs[] = { &normalitalicserif, &leftfacingitalicserif, &leftfacingitalicserif };
4608 static struct italicserifdata *boldserifs[] = { &bolditalicserif, &boldleftfacingitalicserif, &boldleftfacingitalicserif };
4609 
InterpBp(BasePoint * bp,int index,double xscale,double yscale,double interp,double endx,struct italicserifdata * normal,struct italicserifdata * bold)4610 static void InterpBp(BasePoint *bp, int index, double xscale, double yscale,
4611 	double interp, double endx, struct italicserifdata *normal,struct italicserifdata  *bold) {
4612     bp->x = xscale * ((1-interp)*normal->points[index].x + interp*bold->points[index].x) + endx;
4613     bp->y = yscale * ((1-interp)*normal->points[index].y + interp*bold->points[index].y);
4614 }
4615 
MakeBottomItalicSerif(double stemwidth,double endx,ItalicInfo * ii,int seriftype)4616 static SplineSet *MakeBottomItalicSerif(double stemwidth,double endx,
4617 	ItalicInfo *ii, int seriftype) {
4618     double xscale, yscale, interp;
4619     BasePoint bp;
4620     int i;
4621     SplinePoint *last, *cur;
4622     SplineSet *ss;
4623     struct italicserifdata *normal, *bold;
4624 
4625     normal = normalserifs[seriftype];
4626     bold   =   boldserifs[seriftype];
4627 
4628     if ( stemwidth<0 )
4629 	stemwidth = -stemwidth;
4630 
4631     xscale = ii->emsize/1000.0;
4632     interp = (stemwidth/xscale - normal->stemwidth)/
4633 	    (bold->stemwidth-normal->stemwidth);
4634     yscale = ii->x_height/normal->xheight;
4635 
4636     ss = chunkalloc(sizeof(SplineSet));
4637     i=0;
4638     InterpBp(&bp,i,xscale,yscale,interp,endx,normal,bold);
4639     ss->first = last = SplinePointCreate(bp.x,bp.y);
4640     for ( ++i; normal->points[i].type!=pt_end; ) {
4641 	if ( normal->points[i].type==pt_oncurve ) {
4642 	    InterpBp(&bp,i,xscale,yscale,interp,endx,normal,bold);
4643 	    cur = SplinePointCreate(bp.x,bp.y);
4644 	    SplineMake3(last,cur);
4645 	    ++i;
4646 	} else {
4647 	    InterpBp(&last->nextcp,i,xscale,yscale,interp,endx,normal,bold);
4648 	    last->nonextcp = false;
4649 	    i+=2;
4650 	    InterpBp(&bp,i,xscale,yscale,interp,endx,normal,bold);
4651 	    cur = SplinePointCreate(bp.x,bp.y);
4652 	    InterpBp(&cur->prevcp,i-1,xscale,yscale,interp,endx,normal,bold);
4653 	    cur->noprevcp = false;
4654 	    SplineMake3(last,cur);
4655 	    ++i;
4656 	}
4657 	last = cur;
4658     }
4659     ss->last = last;
4660     if ( ii->order2 ) {
4661 	SplineSet *newss;
4662 	SplineSetsRound2Int(ss,1.0,false,false);
4663 	newss = SSttfApprox(ss);
4664 	SplinePointListFree(ss);
4665 	ss = newss;
4666     } else {
4667 	SPLCategorizePoints(ss);
4668     }
4669     { double temp;
4670 	if ( (temp = ss->first->me.x-ss->last->me.x)<0 ) temp = -temp;
4671 	if ( seriftype==0 && !RealWithin(temp,stemwidth,.1))
4672 	    IError( "Stem width doesn't match serif" );
4673     }
4674 return( ss );
4675 }
4676 
MakeTopItalicSerif(double stemwidth,double endx,ItalicInfo * ii,int at_xh)4677 static SplineSet *MakeTopItalicSerif(double stemwidth,double endx,
4678 	ItalicInfo *ii,int at_xh) {
4679     SplineSet *ss = MakeBottomItalicSerif(stemwidth,0,ii,0);
4680     real trans[6];
4681 
4682     memset(trans,0,sizeof(trans));
4683     trans[0] = trans[3] = -1;
4684     trans[4] = endx; trans[5] = at_xh ? ii->x_height : ii->ascender_height;
4685 return( SplinePointListTransform(ss,trans,tpt_AllPoints));
4686 }
4687 
MakeItalicDSerif(DStemInfo * d,double stemwidth,double endx,ItalicInfo * ii,int seriftype,int top)4688 static SplineSet *MakeItalicDSerif(DStemInfo *d,double stemwidth,
4689 	double endx, ItalicInfo *ii,int seriftype, int top) {
4690     SplineSet *ss;
4691     real trans[6];
4692     int i;
4693     double spos, epos, dpos;
4694     extended t1, t2;
4695     SplinePoint *sp;
4696     Spline *s;
4697     double cur_sw;
4698     int order2 = ii->order2;
4699 
4700     ii->order2 = false;		/* Don't convert to order2 untill after */
4701     ss = MakeBottomItalicSerif(stemwidth,0,ii,seriftype);
4702     ii->order2 = order2;	/* We finish messing with the serif */
4703 
4704     memset(trans,0,sizeof(trans));
4705 
4706     if ( top ) {
4707 	trans[0] = trans[3] = -1;
4708 	trans[4] = endx; trans[5] = top==1 ? ii->x_height : ii->ascender_height;
4709 	SplinePointListTransform(ss,trans,tpt_AllPoints);
4710     }
4711     /* given the orientation of the dstem, do we need to flip the serif? */
4712     if ( (seriftype==0 && d->unit.x*d->unit.y>0) || (seriftype!=0 && d->unit.x*d->unit.y<0)) {
4713 	trans[0] = -1; trans[3] = 1;
4714 	trans[4] = ss->first->me.x+endx;
4715 	trans[5] = 0;
4716 	SplinePointListTransform(ss,trans,tpt_AllPoints);
4717 	SplineSetReverse(ss);
4718     }
4719 
4720     /* Now we want to find the places near the start and end of the serif */
4721     /*  where it is parallel to the stem. We want to truncate the serif   */
4722     /*  at those points, so that the serif now starts and ends parallel to*/
4723     /*  the stem. */
4724     /* How to do that? rotate it parallel to the stem and then look for */
4725     /*  min/max points */
4726     trans[0] = trans[3] = d->unit.x;
4727     trans[2] = -(trans[1] = -d->unit.y);
4728     trans[4] = trans[5] = 0;
4729     SplinePointListTransform(ss,trans,tpt_AllPoints);
4730 
4731     /* Now the min/max point will probably be on the first spline, but might */
4732     /*  be on the second (and will probably be on the last spline, but might */
4733     /*  be on the penultimate) */
4734     s = ss->first->next;
4735     for ( i=0; i<2; ++i ) {
4736 	SplineFindExtrema(&s->splines[1],&t1,&t2);
4737 	if ( t1>=0 && t1<=1 ) {
4738 	    if ( s!=ss->first->next ) {
4739 		SplineFree(ss->first->next);
4740 		SplinePointFree(ss->first);
4741 		ss->first = s->from;
4742 		ss->start_offset = 0;
4743 	    }
4744 	    if ( t1>=.999 ) {
4745 		SplinePointFree(ss->first);
4746 		ss->first = s->to;
4747 		ss->start_offset = 0;
4748 		SplineFree(ss->first->prev);
4749 		ss->first->prev = NULL;
4750 	    } else if ( t1>.001 ) {
4751 		sp = SplineBisect(s,t1);
4752 		SplinePointFree(ss->first);
4753 		SplineFree(sp->prev);
4754 		sp->prev = NULL;
4755 		ss->first = sp;
4756 		ss->start_offset = 0;
4757 	    }
4758     break;
4759 	}
4760 	s=s->to->next;
4761     }
4762     s = ss->last->prev;
4763     for ( i=0; i<2; ++i ) {
4764 	SplineFindExtrema(&s->splines[1],&t1,&t2);
4765 	if ( t1>=0 && t1<=1 ) {
4766 	    if ( s!=ss->last->prev ) {
4767 		SplineFree(ss->last->prev);
4768 		SplinePointFree(ss->last);
4769 		ss->last = s->to;
4770 	    }
4771 	    if ( t1<=.001 ) {
4772 		SplinePointFree(ss->last);
4773 		ss->last = s->from;
4774 		SplineFree(ss->last->next);
4775 		ss->last->next = NULL;
4776 	    } else if ( t1<.999 ) {
4777 		sp = SplineBisect(s,t1);
4778 		SplinePointFree(ss->last);
4779 		SplineFree(sp->next);
4780 		sp->next = NULL;
4781 		ss->last = sp;
4782 	    }
4783     break;
4784 	}
4785 	s = s->from->prev;
4786     }
4787     /* Now rotate it back to the correct orientation */
4788     trans[2] = -trans[2]; trans[1] = -trans[1];
4789     SplinePointListTransform(ss,trans,tpt_AllPoints);
4790 
4791     /* Now it probably doesn't have the correct stemwidth */
4792     cur_sw = (ss->first->me.x-ss->last->me.x)*d->unit.y - (ss->first->me.y-ss->last->me.y)*d->unit.x;
4793     if ( cur_sw<0 ) cur_sw = -cur_sw;
4794     if ( cur_sw!=stemwidth ) {
4795 	double diff3 = (cur_sw-stemwidth)/3;
4796 	if ( ss->first->me.x*d->unit.y-ss->first->me.y*d->unit.x <
4797 		ss->last->me.x*d->unit.y-ss->last->me.y*d->unit.x )
4798 	    diff3 = -diff3;
4799 	ss->first->me.x -= diff3*d->unit.y;
4800 	ss->first->me.y -= -diff3*d->unit.x;
4801 	ss->first->nextcp.x -= diff3*d->unit.y;
4802 	ss->first->nextcp.y -= -diff3*d->unit.x;
4803 	SplineRefigure(ss->first->next);
4804 	ss->last->me.x += 2*diff3*d->unit.y;
4805 	ss->last->me.y += -2*diff3*d->unit.x;
4806 	ss->last->prevcp.x += 2*diff3*d->unit.y;
4807 	ss->last->prevcp.y += -2*diff3*d->unit.x;
4808 	SplineRefigure(ss->last->prev);
4809     }
4810 
4811     /* Finally, position so that the serif lies on the dstem. We've already */
4812     /*  given it the correct height, so all we can adjust is the x value */
4813     /* (We played with the x-value earlier, but that only worked if we didn't */
4814     /*  need to truncate anything) */
4815     memset(trans,0,sizeof(trans));
4816     trans[0] = trans[3] = 1;
4817 
4818     spos = d->left .x + (ss->first->me.y-d->left .y)*d->unit.x/d->unit.y;
4819     epos = d->right.x + (ss->first->me.y-d->right.y)*d->unit.x/d->unit.y;
4820     dpos = ss->first->me.x + (ss->last->me.y-ss->first->me.y)*d->unit.x/d->unit.y;
4821     if ( dpos>ss->last->me.x ) {
4822 	/* ss->last is to the left of ss->first */
4823 	/* move ss->first to the rightmost edge of the dstem */
4824 	if ( spos>epos )
4825 	    trans[4] = spos-ss->first->me.x;
4826 	else
4827 	    trans[4] = epos-ss->first->me.x;
4828     } else {
4829 	if ( spos<epos )
4830 	    trans[4] = spos-ss->first->me.x;
4831 	else
4832 	    trans[4] = epos-ss->first->me.x;
4833     }
4834     SplinePointListTransform(ss,trans,tpt_AllPoints);
4835     if ( ii->order2 ) {
4836 	SplineSet *newss;
4837 	SplineSetsRound2Int(ss,1.0,false,false);
4838 	newss = SSttfApprox(ss);
4839 	SplinePointListFree(ss);
4840 	ss = newss;
4841     } else {
4842 	SPLCategorizePoints(ss);
4843     }
4844 return( ss );
4845 }
4846 
InHintRange(HintInstance * hi,double pos)4847 static int InHintRange(HintInstance *hi, double pos) {
4848 
4849     for ( ; hi!=NULL; hi=hi->next ) {
4850 	if ( pos>=hi->begin && pos<=hi->end )
4851 return( true );
4852     }
4853 return( false );
4854 }
4855 
ValidBottomSerif(SplinePoint * start,SplinePoint * end,double depth,double fuzz,double minbound,double maxbound)4856 static double ValidBottomSerif(SplinePoint *start,SplinePoint *end,
4857 	double depth, double fuzz, double minbound, double maxbound ) {
4858     double max = start->me.y>end->me.y ? start->me.y : end->me.y;
4859     SplinePoint *sp, *last;
4860     int got_down = false, got_up=false;
4861     /* To be a serif it must go down about as far down as we expect       */
4862     /*  (baseline, or the bottom of descenders), it can bounce around     */
4863     /*  a little down there, but it can't go too far down. Once it starts */
4864     /*  going significantly up, it must continue going up until the end.  */
4865 
4866     if ( start==end )
4867 return( false );
4868 
4869     last = NULL;
4870     for ( sp=start ; ; ) {
4871 	if ( sp->me.x<minbound || sp->me.x>maxbound )
4872 return( false );
4873 	if ( sp->me.y > max+fuzz )
4874 return( false );
4875 	if ( sp->me.y < depth-fuzz )
4876 return( false );
4877 	if ( sp->me.y < depth+fuzz/2+1 )
4878 	    got_down = true;
4879 	else if ( got_down && sp->me.y > depth+fuzz/2 )
4880 	    got_up = true;
4881 	if ( last!=NULL ) {
4882 	    if ( !got_down && sp->me.y>last->me.y+fuzz/10 )
4883 return( false );
4884 	    else if ( got_up && sp->me.y<last->me.y-fuzz/10 )
4885 return( false );
4886 	}
4887 	last = sp;
4888 	if ( sp==end )
4889 return( got_down );
4890 	if ( sp->next==NULL )
4891 return( false );
4892 	sp = sp->next->to;
4893     }
4894 }
4895 
FindBottomSerifOnStem(SplineChar * sc,int layer,StemInfo * h,double depth,ItalicInfo * ii,SplinePoint ** _start,SplinePoint ** _end,SplineSet ** _ss)4896 static void FindBottomSerifOnStem(SplineChar *sc,int layer,StemInfo *h,
4897 	double depth, ItalicInfo *ii,
4898 	SplinePoint **_start,SplinePoint **_end, SplineSet **_ss) {
4899     SplinePoint *start=NULL, *end=NULL, *sp;
4900     SplinePointList *ss;
4901     double sdiff, ediff;
4902     double fuzz = (sc->parent->ascent+sc->parent->descent)/100.0;
4903 
4904     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
4905 	start=end=NULL;
4906 	for ( sp=ss->first; ; ) {
4907 	    if ( ( sdiff = sp->me.x-h->start)<0 ) sdiff = -sdiff;
4908 	    if ( ( ediff = sp->me.x-(h->start+h->width))<0 ) ediff = -ediff;
4909 	    if ( sdiff<=3 && ( start==NULL ||
4910 		    ( sp->me.y<start->me.y && (InHintRange(h->where,sp->me.y) || !InHintRange(h->where,start->me.y))))) {
4911 		start=sp;
4912 	    } else if ( ediff<=3 && ( end==NULL ||
4913 		    ( sp->me.y<end->me.y && (InHintRange(h->where,sp->me.y) || !InHintRange(h->where,end->me.y)) ))) {
4914 		end=sp;
4915 	    }
4916 	    if ( sp->next==NULL )
4917 	break;
4918 	    sp = sp->next->to;
4919 	    if ( sp==ss->first )
4920 	break;
4921 	}
4922 	if ( sp->next==NULL )
4923     continue;
4924 	if ( start!=NULL && end!=NULL ) {
4925 	    if ( ValidBottomSerif(start,end,depth,fuzz,h->start-ii->serif_extent-fuzz,h->start+h->width+ii->serif_extent+fuzz))
4926     break;
4927 	    else if ( ValidBottomSerif(end,start,depth,fuzz,h->start-ii->serif_extent-fuzz,h->start+h->width+ii->serif_extent+fuzz)) {
4928 		SplinePoint *temp = start;
4929 		start = end;
4930 		end = temp;
4931     break;
4932 	    } else
4933 		start = NULL;
4934 	}
4935     }
4936 
4937     if ( start==NULL || end==NULL )
4938 	start = end = NULL;
4939     *_start = start;
4940     *_end   = end;
4941     *_ss    = ss;
4942 }
4943 
ValidBottomDSerif(SplinePoint * start,SplinePoint * end,double depth,double fuzz,ItalicInfo * ii,DStemInfo * d)4944 static double ValidBottomDSerif(SplinePoint *start,SplinePoint *end,
4945 	double depth, double fuzz, ItalicInfo *ii, DStemInfo *d ) {
4946     double max = start->me.y>end->me.y ? start->me.y : end->me.y;
4947     SplinePoint *sp, *last;
4948     int got_down = false, got_up=false;
4949     /* To be a serif it must go down about as far down as we expect       */
4950     /*  (baseline, or the bottom of descenders), it can bounce around     */
4951     /*  a little down there, but it can't go too far down. Once it starts */
4952     /*  going significantly up, it must continue going up until the end.  */
4953     double dlpos, drpos;
4954 
4955     if ( start==end )
4956 return( false );
4957 
4958     last = NULL;
4959     for ( sp=start ; ; ) {
4960 	dlpos = (sp->me.x-d->left .x)*d->unit.y - (sp->me.y-d->left .y)*d->unit.x;
4961 	drpos = (sp->me.x-d->right.x)*d->unit.y - (sp->me.y-d->right.y)*d->unit.x;
4962 	if ( dlpos< -1.5*ii->serif_extent-fuzz || drpos > 1.5*ii->serif_extent+fuzz )
4963 return( false );
4964 	if ( sp->me.y > max+fuzz )
4965 return( false );
4966 	if ( sp->me.y < depth-fuzz )
4967 return( false );
4968 	if ( sp->me.y < depth+fuzz/2+1 )
4969 	    got_down = true;
4970 	else if ( got_down && sp->me.y > depth+fuzz/2 )
4971 	    got_up = true;
4972 	if ( last!=NULL ) {
4973 	    if ( !got_down && sp->me.y>last->me.y+fuzz/10 )
4974 return( false );
4975 	    else if ( got_up && sp->me.y<last->me.y-fuzz/10 )
4976 return( false );
4977 	}
4978 	last = sp;
4979 	if ( sp==end )
4980 return( got_down );
4981 	if ( sp->next==NULL )
4982 return( false );
4983 	sp = sp->next->to;
4984     }
4985 }
4986 
ValidTopDSerif(SplinePoint * start,SplinePoint * end,double height,double fuzz,ItalicInfo * ii,DStemInfo * d)4987 static double ValidTopDSerif(SplinePoint *start,SplinePoint *end,
4988 	double height, double fuzz, ItalicInfo *ii, DStemInfo *d ) {
4989     double min = start->me.y<end->me.y ? start->me.y : end->me.y;
4990     SplinePoint *sp, *last;
4991     int got_down = false, got_up=false;
4992     double dlpos, drpos;
4993 
4994     if ( start==end )
4995 return( false );
4996 
4997     last = NULL;
4998     for ( sp=start ; ; ) {
4999 	dlpos = (sp->me.x-d->left .x)*d->unit.y - (sp->me.y-d->left .y)*d->unit.x;
5000 	drpos = (sp->me.x-d->right.x)*d->unit.y - (sp->me.y-d->right.y)*d->unit.x;
5001 	if ( dlpos< -1.5*ii->serif_extent-fuzz || drpos > 1.5*ii->serif_extent+fuzz )
5002 return( false );
5003 	if ( sp->me.y < min-fuzz )
5004 return( false );
5005 	if ( sp->me.y > height+2*fuzz )
5006 return( false );
5007 	if ( sp->me.y > height-fuzz/2 )
5008 	    got_up = true;
5009 	else if ( got_up && sp->me.y < height-fuzz/2-1 )
5010 	    got_down = true;
5011 	if ( last!=NULL ) {
5012 	    if ( !got_up && sp->me.y<last->me.y-fuzz/2 )
5013 return( false );
5014 	    else if ( got_down && sp->me.y>last->me.y+fuzz/2 )
5015 return( false );
5016 	}
5017 	last = sp;
5018 	if ( sp==end )
5019 return( got_up );
5020 	if ( sp->next==NULL )
5021 return( false );
5022 	sp = sp->next->to;
5023     }
5024 }
5025 
RoughlyParallel(SplinePoint * sp,BasePoint * unit)5026 static int RoughlyParallel(SplinePoint *sp,BasePoint *unit) {
5027     BasePoint diff;
5028     double len, off;
5029 
5030     if ( sp->nonextcp && sp->next!=NULL ) {
5031 	diff.x = sp->next->to->me.x - sp->me.x;
5032 	diff.y = sp->next->to->me.y - sp->me.y;
5033     } else {
5034 	diff.x = sp->nextcp.x - sp->me.x;
5035 	diff.y = sp->nextcp.y - sp->me.y;
5036     }
5037     len = sqrt(diff.x*diff.x + diff.y*diff.y);
5038     if ( len!=0 ) {
5039 	if ( (off = (diff.x*unit->y - diff.y*unit->x)/len) < 0 ) off = -off;
5040 	if ( off<.04 )
5041 return( true );
5042     }
5043 
5044     if ( sp->noprevcp && sp->prev!=NULL ) {
5045 	diff.x = sp->prev->from->me.x - sp->me.x;
5046 	diff.y = sp->prev->from->me.y - sp->me.y;
5047     } else {
5048 	diff.x = sp->prevcp.x - sp->me.x;
5049 	diff.y = sp->prevcp.y - sp->me.y;
5050     }
5051     len = sqrt(diff.x*diff.x + diff.y*diff.y);
5052     if ( len!=0 ) {
5053 	if ( (off = (diff.x*unit->y - diff.y*unit->x)/len) < 0 ) off = -off;
5054 	if ( off<.04 )
5055 return( true );
5056     }
5057 return( false );
5058 }
5059 
FindBottomSerifOnDStem(SplineChar * sc,int layer,DStemInfo * d,double depth,ItalicInfo * ii,SplinePoint ** _start,SplinePoint ** _end,SplineSet ** _ss)5060 static void FindBottomSerifOnDStem(SplineChar *sc,int layer,DStemInfo *d,
5061 	double depth, ItalicInfo *ii,
5062 	SplinePoint **_start,SplinePoint **_end, SplineSet **_ss) {
5063     SplinePoint *start=NULL, *end=NULL, *sp;
5064     SplinePointList *ss;
5065     double sdiff, ediff;
5066     double pos;
5067     double fuzz = (sc->parent->ascent+sc->parent->descent)/100.0;
5068 
5069     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
5070 	start=end=NULL;
5071 	for ( sp=ss->first; ; ) {
5072 	    if ( RoughlyParallel(sp,&d->unit)) {
5073 		pos = (sp->me.x-d->left.x)*d->unit.x + (sp->me.y-d->left.y)*d->unit.y;
5074 		if ( ( sdiff = (sp->me.x-d->left.x)*d->unit.y - (sp->me.y-d->left.y)*d->unit.x)<0 ) sdiff = -sdiff;
5075 		if ( ( ediff = (sp->me.x-d->right.x)*d->unit.y - (sp->me.y-d->right.y)*d->unit.x)<0 ) ediff = -ediff;
5076 		/* Hint Ranges for diagonals seem not to be what we want here */
5077 		if ( sdiff<=10 && ( start==NULL ||
5078 			( sp->me.y<start->me.y /*&& (InHintRange(d->where,pos) || !InHintRange(d->where,spos))*/ ))) {
5079 		    start=sp;
5080 		} else if ( ediff<=10 && ( end==NULL ||
5081 			( sp->me.y<end->me.y /*&& (InHintRange(d->where,pos) || !InHintRange(d->where,epos))*/ ))) {
5082 		    end=sp;
5083 		}
5084 	    }
5085 	    if ( sp->next==NULL )
5086 	break;
5087 	    sp = sp->next->to;
5088 	    if ( sp==ss->first )
5089 	break;
5090 	}
5091 	if ( sp->next==NULL )
5092     continue;
5093 	if ( start!=NULL && end!=NULL ) {
5094 	    if ( ValidBottomDSerif(start,end,depth,fuzz,ii,d))
5095     break;
5096 	    else if ( ValidBottomDSerif(end,start,depth,fuzz,ii,d)) {
5097 		SplinePoint *temp = start;
5098 		start = end;
5099 		end = temp;
5100     break;
5101 	    } else
5102 		start = NULL;
5103 	}
5104     }
5105 
5106     if ( start==NULL || end==NULL )
5107 	start = end = NULL;
5108     *_start = start;
5109     *_end   = end;
5110     *_ss    = ss;
5111 }
5112 
5113 
FindTopSerifOnDStem(SplineChar * sc,int layer,DStemInfo * d,double height,ItalicInfo * ii,SplinePoint ** _start,SplinePoint ** _end,SplineSet ** _ss)5114 static void FindTopSerifOnDStem(SplineChar *sc,int layer,DStemInfo *d,
5115 	double height, ItalicInfo *ii,
5116 	SplinePoint **_start,SplinePoint **_end, SplineSet **_ss) {
5117     SplinePoint *start=NULL, *end=NULL, *sp;
5118     SplinePointList *ss;
5119     double sdiff, ediff;
5120     double pos;
5121     double fuzz = (sc->parent->ascent+sc->parent->descent)/100.0;
5122 
5123     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
5124 	start=end=NULL;
5125 	for ( sp=ss->first; ; ) {
5126 	    if ( RoughlyParallel(sp,&d->unit)) {
5127 		pos = (sp->me.x-d->left.x)*d->unit.x + (sp->me.y-d->left.y)*d->unit.y;
5128 		if ( ( sdiff = (sp->me.x-d->left.x)*d->unit.y - (sp->me.y-d->left.y)*d->unit.x)<0 ) sdiff = -sdiff;
5129 		if ( ( ediff = (sp->me.x-d->right.x)*d->unit.y - (sp->me.y-d->right.y)*d->unit.x)<0 ) ediff = -ediff;
5130 		/* Hint Ranges for diagonals seem not to be what we want here */
5131 		if ( sdiff<=10 && ( start==NULL ||
5132 			( sp->me.y>start->me.y /*&& (InHintRange(d->where,pos) || !InHintRange(d->where,spos))*/ ))) {
5133 		    start=sp;
5134 		} else if ( ediff<=10 && ( end==NULL ||
5135 			( sp->me.y>end->me.y /*&& (InHintRange(d->where,pos) || !InHintRange(d->where,epos))*/ ))) {
5136 		    end=sp;
5137 		}
5138 	    }
5139 	    if ( sp->next==NULL )
5140 	break;
5141 	    sp = sp->next->to;
5142 	    if ( sp==ss->first )
5143 	break;
5144 	}
5145 	if ( sp->next==NULL )
5146     continue;
5147 	if ( start!=NULL && end!=NULL ) {
5148 	    if ( ValidTopDSerif(start,end,height,fuzz,ii,d))
5149     break;
5150 	    else if ( ValidTopDSerif(end,start,height,fuzz,ii,d)) {
5151 		SplinePoint *temp = start;
5152 		start = end;
5153 		end = temp;
5154     break;
5155 	    } else
5156 		start = NULL;
5157 	}
5158     }
5159 
5160     if ( start==NULL || end==NULL )
5161 	start = end = NULL;
5162     *_start = start;
5163     *_end   = end;
5164     *_ss    = ss;
5165 }
5166 
SerifRemove(SplinePoint * start,SplinePoint * end,SplineSet * ss)5167 static void SerifRemove(SplinePoint *start,SplinePoint *end,SplineSet *ss) {
5168     SplinePoint *mid, *spnext;
5169 
5170     for ( mid=start; mid!=end; mid=spnext ) {
5171 	spnext = mid->next->to;
5172 	if ( mid!=start ) {
5173 	    SplinePointFree(mid);
5174 	    if ( mid==ss->first ) {
5175 		ss->first = ss->last = start;
5176 		ss->start_offset = 0;
5177 	    }
5178 	}
5179 	SplineFree(spnext->prev);
5180     }
5181     start->next = end->prev = NULL;
5182     start->nonextcp = end->noprevcp = true;
5183 }
5184 
StemMoveBottomEndTo(SplinePoint * sp,double y,int is_start)5185 static SplinePoint *StemMoveBottomEndTo(SplinePoint *sp,double y,int is_start) {
5186     SplinePoint *other;
5187 
5188     if ( is_start ) {
5189 	if ( sp->noprevcp || y>=sp->me.y ) {
5190 	    sp->prevcp.y += (y-sp->me.y);
5191 	    if ( sp->prev->order2 && !sp->prev->from->nonextcp )
5192 		sp->prev->from->nextcp = sp->prevcp;
5193 	    sp->me.y = y;
5194 	    SplineRefigure(sp->prev);
5195 	} else {
5196 	    other = SplinePointCreate(sp->me.x,y);
5197 	    sp->nonextcp = true;
5198 	    SplineMake(sp,other,sp->prev->order2);
5199 	    sp = other;
5200 	}
5201     } else {
5202 	if ( sp->nonextcp || y>=sp->me.y ) {
5203 	    sp->nextcp.y += (y-sp->me.y);
5204 	    if ( sp->next->order2 && !sp->next->to->noprevcp )
5205 		sp->next->to->prevcp = sp->nextcp;
5206 	    sp->me.y = y;
5207 	    SplineRefigure(sp->next);
5208 	} else {
5209 	    other = SplinePointCreate(sp->me.x,y);
5210 	    sp->noprevcp = true;
5211 	    SplineMake(other,sp,sp->next->order2);
5212 	    sp = other;
5213 	}
5214     }
5215 return( sp );
5216 }
5217 
StemMoveDBottomEndTo(SplinePoint * sp,double y,DStemInfo * d,int is_start)5218 static SplinePoint *StemMoveDBottomEndTo(SplinePoint *sp,double y,DStemInfo *d,
5219 	int is_start) {
5220     SplinePoint *other;
5221     double xoff;
5222 
5223     xoff = (y-sp->me.y)*d->unit.x/d->unit.y;
5224     if ( is_start ) {
5225 	if ( sp->noprevcp || y>=sp->me.y ) {
5226 	    sp->prevcp.y += (y-sp->me.y);
5227 	    sp->prevcp.x += xoff;
5228 	    if ( sp->prev->order2 && !sp->prev->from->nonextcp )
5229 		sp->prev->from->nextcp = sp->prevcp;
5230 	    sp->me.y = y;
5231 	    sp->me.x += xoff;
5232 	    SplineRefigure(sp->prev);
5233 	} else {
5234 	    other = SplinePointCreate(sp->me.x+xoff,y);
5235 	    sp->nonextcp = true;
5236 	    SplineMake(sp,other,sp->prev->order2);
5237 	    sp = other;
5238 	}
5239     } else {
5240 	if ( sp->nonextcp || y>=sp->me.y ) {
5241 	    sp->nextcp.y += (y-sp->me.y);
5242 	    sp->nextcp.x += xoff;
5243 	    if ( sp->next->order2 && !sp->next->to->noprevcp )
5244 		sp->next->to->prevcp = sp->nextcp;
5245 	    sp->me.y = y;
5246 	    sp->me.x += xoff;
5247 	    SplineRefigure(sp->next);
5248 	} else {
5249 	    other = SplinePointCreate(sp->me.x+xoff,y);
5250 	    sp->noprevcp = true;
5251 	    SplineMake(other,sp,sp->next->order2);
5252 	    sp = other;
5253 	}
5254     }
5255 return( sp );
5256 }
5257 
StemMoveBottomEndCarefully(SplinePoint * sp,SplineSet * oldss,SplineSet * ss,DStemInfo * d,int is_start)5258 static SplinePoint *StemMoveBottomEndCarefully(SplinePoint *sp,SplineSet *oldss,
5259 	SplineSet *ss,DStemInfo *d, int is_start) {
5260     SplinePoint *other = is_start ? ss->first : ss->last;
5261 
5262     if ( is_start ) {
5263 	if ( sp->me.y<other->me.y &&
5264 		(( sp->noprevcp && other->me.y>sp->prev->from->me.y) ||
5265 		 (!sp->noprevcp && other->me.y>sp->prevcp.y)) ) {
5266 	    /* We need to move sp up, but we can't because it turns down */
5267 	    /*  So instead, move "other" down to sp */
5268 	    extended ts[3];
5269 	    /* Well, we might be able to move it up a little... */
5270 	    if ( sp->prev->from->me.x==sp->me.x ) {
5271 		SplinePoint *newsp = sp->prev->from;
5272 		SplineFree(sp->prev);
5273 		SplinePointFree(sp);
5274 		if ( sp==oldss->first ) {
5275 		    oldss->first = oldss->last = newsp;
5276 		    oldss->start_offset = 0;
5277 		}
5278 		sp=newsp;
5279 	    }
5280 	    CubicSolve(&other->next->splines[1],sp->me.y,ts);
5281 	    if ( ts[0]!=-1 ) {
5282 		SplinePoint *newend = SplineBisect(other->next,ts[0]);
5283 		SplineFree(newend->prev);
5284 		SplinePointFree(other);
5285 		newend->prev = NULL;
5286 		newend->nextcp.x += sp->me.x-newend->me.x;
5287 		if ( newend->next->order2 && !newend->nonextcp )
5288 		    newend->next->to->prevcp = newend->nextcp;
5289 		newend->me.x = sp->me.x;
5290 		ss->first = newend;
5291 		ss->start_offset = 0;
5292 return( sp );
5293 	    }
5294 	}
5295     } else {
5296 	if ( sp->me.y<other->me.y &&
5297 		(( sp->nonextcp && other->me.y>sp->next->to->me.y) ||
5298 		 (!sp->nonextcp && other->me.y>sp->nextcp.y)) ) {
5299 	    extended ts[3];
5300 	    if ( sp->next->to->me.x==sp->me.x ) {
5301 		SplinePoint *newsp = sp->next->to;
5302 		SplineFree(sp->next);
5303 		SplinePointFree(sp);
5304 		if ( sp==oldss->first ) {
5305 		    oldss->first = oldss->last = newsp;
5306 		    oldss->start_offset = 0;
5307 		}
5308 		sp=newsp;
5309 	    }
5310 	    CubicSolve(&other->prev->splines[1],sp->me.y,ts);
5311 	    if ( ts[0]!=-1 ) {
5312 		SplinePoint *newend = SplineBisect(other->prev,ts[0]);
5313 		SplineFree(newend->next);
5314 		SplinePointFree(other);
5315 		newend->next = NULL;
5316 		newend->prevcp.x += sp->me.x-newend->me.x;
5317 		if ( newend->prev->order2 && !newend->noprevcp )
5318 		    newend->prev->from->nextcp = newend->prevcp;
5319 		newend->me.x = sp->me.x;
5320 		ss->last = newend;
5321 return( sp );
5322 	    }
5323 	}
5324     }
5325     if ( d==NULL )
5326 return( StemMoveBottomEndTo(sp,other->me.y,is_start));
5327     else
5328 return( StemMoveDBottomEndTo(sp,other->me.y,d,is_start));
5329 }
5330 
DeSerifBottomStem(SplineChar * sc,int layer,StemInfo * h,ItalicInfo * ii,double y,SplinePoint ** _start,SplinePoint ** _end)5331 static void DeSerifBottomStem(SplineChar *sc,int layer,StemInfo *h,ItalicInfo *ii,
5332 	double y, SplinePoint **_start, SplinePoint **_end) {
5333     SplinePoint *start, *end, *mid;
5334     SplineSet *ss;
5335 
5336     if ( _start!=NULL )
5337 	*_start = *_end = NULL;
5338 
5339     if ( h==NULL )
5340 return;
5341     FindBottomSerifOnStem(sc,layer,h,y,ii,&start,&end,&ss);
5342     if ( start==NULL || end==NULL || start==end )
5343 return;
5344     SerifRemove(start,end,ss);
5345 
5346     if ( ii->secondary_serif == srf_flat ) {
5347 	start = StemMoveBottomEndTo(start,y,true);
5348 	end = StemMoveBottomEndTo(end,y,false);
5349 	start->nonextcp = end->noprevcp = true;
5350 	SplineMake(start,end,sc->layers[layer].order2);
5351     } else if ( ii->secondary_serif == srf_simpleslant ) {
5352 	if ( ii->tan_ia<0 ) {
5353 	    start = StemMoveBottomEndTo(start,y+ (end->me.x-start->me.x)*ii->tan_ia,true);
5354 	    end = StemMoveBottomEndTo(end,y,false);
5355 	} else {
5356 	    start = StemMoveBottomEndTo(start,y,true);
5357 	    end = StemMoveBottomEndTo(end,y - (end->me.x-start->me.x)*ii->tan_ia,false);
5358 	}
5359 	start->nonextcp = end->noprevcp = true;
5360 	SplineMake(start,end,sc->layers[layer].order2);
5361     } else {
5362 	if ( ii->tan_ia<0 ) {
5363 	    start = StemMoveBottomEndTo(start,y+ .8*(end->me.x-start->me.x)*ii->tan_ia,true);
5364 	    end = StemMoveBottomEndTo(end,y+ .2*(end->me.x-start->me.x)*ii->tan_ia,false);
5365 	    mid = SplinePointCreate(.2*start->me.x+.8*end->me.x,y);
5366 	} else {
5367 	    start = StemMoveBottomEndTo(start,y- .2*(end->me.x-start->me.x)*ii->tan_ia,true);
5368 	    end = StemMoveBottomEndTo(end,y- .8*(end->me.x-start->me.x)*ii->tan_ia,false);
5369 	    mid = SplinePointCreate(.2*end->me.x+.8*start->me.x,y);
5370 	}
5371 	start->nonextcp = end->noprevcp = true;
5372 	mid->pointtype = pt_corner;
5373 	SplineMake(start,mid,sc->layers[layer].order2);
5374 	SplineMake(mid,end,sc->layers[layer].order2);
5375     }
5376     start->pointtype = end->pointtype = pt_corner;
5377     if ( _start!=NULL ) {
5378 	*_start = start;
5379 	*_end = end;
5380     }
5381 }
5382 
DeSerifDescender(SplineChar * sc,int layer,ItalicInfo * ii)5383 static void DeSerifDescender(SplineChar *sc,int layer,ItalicInfo *ii) {
5384     /* sc should only have one descender. Find it */
5385     StemInfo *h;
5386     int i;
5387     HintInstance *hi;
5388     StemInfo *smallest=NULL;
5389 
5390     for ( i=0, h=sc->vstem; h!=NULL; ++i, h=h->next ) {
5391 	for ( hi=h->where; hi!=NULL; hi=hi->next )
5392 	    if ( hi->begin<0 || hi->end<0 ) {
5393 		if ( smallest==NULL || h->width<smallest->width ) {
5394 		    smallest = h;
5395 	break;
5396 		}
5397 	    }
5398     }
5399     if ( smallest!=NULL )
5400 	DeSerifBottomStem(sc,layer,smallest,ii,ii->pq_depth,NULL,NULL);
5401 }
5402 
ValidTopSerif(SplinePoint * start,SplinePoint * end,double height,double fuzz,double minbound,double maxbound)5403 static double ValidTopSerif(SplinePoint *start,SplinePoint *end,
5404 	double height, double fuzz, double minbound, double maxbound ) {
5405     double min = start->me.y<end->me.y ? start->me.y : end->me.y;
5406     SplinePoint *sp, *last;
5407     int got_down = false, got_up=false;
5408     /* To be a serif it must go up about as far up as we expect       */
5409     /*  (xheight, or the top of ascenders), it can bounce around     */
5410     /*  a little down there, but it can't go too far down. Once it starts */
5411     /*  going significantly up, it must continue going up until the end.  */
5412 
5413     if ( start==end )
5414 return( false );
5415 
5416     last = NULL;
5417     for ( sp=start ; ; ) {
5418 	if ( sp->me.x<minbound || sp->me.x>maxbound )
5419 return( false );
5420 	if ( sp->me.y < min-fuzz )
5421 return( false );
5422 	if ( sp->me.y > height+2*fuzz )
5423 return( false );
5424 	if ( sp->me.y > height-fuzz/2 )
5425 	    got_up = true;
5426 	else if ( got_up && sp->me.y < height-fuzz/2-1 )
5427 	    got_down = true;
5428 	if ( last!=NULL ) {
5429 	    if ( !got_up && sp->me.y<last->me.y-fuzz/2 )
5430 return( false );
5431 	    else if ( got_down && sp->me.y>last->me.y+fuzz/2 )
5432 return( false );
5433 	}
5434 	last = sp;
5435 	if ( sp==end )
5436 return( got_up );
5437 	if ( sp->next==NULL )
5438 return( false );
5439 	sp = sp->next->to;
5440     }
5441 }
5442 
IsLeftHalfSerif(SplinePoint * start,SplinePoint * end,StemInfo * h)5443 static int IsLeftHalfSerif(SplinePoint *start,SplinePoint *end,
5444 	StemInfo *h) {
5445     const double fuzz = 10;
5446     int wentleft=0;
5447     SplinePoint *sp;
5448 
5449     for ( sp=start; sp!=end; sp=sp->next->to ) {
5450 	if ( sp->me.x > h->start+h->width+fuzz )
5451 return( false );
5452 	if ( sp->me.x < h->start )
5453 	    wentleft = true;
5454     }
5455 return( wentleft );
5456 }
5457 
FindTopSerifOnStem(SplineChar * sc,int layer,StemInfo * h,double height,ItalicInfo * ii,SplinePoint ** _start,SplinePoint ** _end,SplineSet ** _ss)5458 static void FindTopSerifOnStem(SplineChar *sc,int layer,StemInfo *h,
5459 	double height, ItalicInfo *ii,
5460 	SplinePoint **_start,SplinePoint **_end, SplineSet **_ss) {
5461     SplinePoint *start=NULL, *end=NULL, *sp;
5462     SplinePointList *ss;
5463     double sdiff, ediff;
5464     double fuzz = (sc->parent->ascent+sc->parent->descent)/100.0;
5465 
5466     for ( ss=sc->layers[layer].splines; ss!=NULL; ss=ss->next ) {
5467 	start=end=NULL;
5468 	for ( sp=ss->first; ; ) {
5469 	    if ( ( sdiff = sp->me.x-h->start)<0 ) sdiff = -sdiff;
5470 	    if ( ( ediff = sp->me.x-(h->start+h->width))<0 ) ediff = -ediff;
5471 	    if ( sdiff<=3 && ( start==NULL ||
5472 		    ( sp->me.y>start->me.y && (InHintRange(h->where,sp->me.y) || !InHintRange(h->where,start->me.y))))) {
5473 		start=sp;
5474 	    } else if ( ediff<=3 && ( end==NULL ||
5475 		    ( sp->me.y>end->me.y && (InHintRange(h->where,sp->me.y) || !InHintRange(h->where,end->me.y)) ))) {
5476 		end=sp;
5477 	    }
5478 	    if ( sp->next==NULL )
5479 	break;
5480 	    sp = sp->next->to;
5481 	    if ( sp==ss->first )
5482 	break;
5483 	}
5484 	if ( sp->next==NULL )
5485     continue;
5486 	if ( start!=NULL && end!=NULL ) {
5487 	    if ( ValidTopSerif(start,end,height,fuzz,h->start-ii->serif_extent-fuzz,h->start+h->width+ii->serif_extent+fuzz))
5488     break;
5489 	    else if ( ValidTopSerif(end,start,height,fuzz,h->start-ii->serif_extent-fuzz,h->start+h->width+ii->serif_extent+fuzz)) {
5490 		SplinePoint *temp = start;
5491 		start = end;
5492 		end = temp;
5493     break;
5494 	    } else
5495 		start = NULL;
5496 	}
5497     }
5498 
5499     if ( start==NULL || end==NULL ) {
5500 	start = end = NULL;
5501 	ss = NULL;
5502     }
5503     *_start = start;
5504     *_end   = end;
5505     *_ss    = ss;
5506 }
5507 
StemMoveTopEndTo(SplinePoint * sp,double y,int is_start)5508 static SplinePoint *StemMoveTopEndTo(SplinePoint *sp,double y,int is_start) {
5509     SplinePoint *other;
5510 
5511     if ( is_start ) {
5512 	if ( sp->noprevcp || y<=sp->me.y ) {
5513 	    sp->prevcp.y += (y-sp->me.y);
5514 	    if ( sp->prev->order2 && !sp->prev->from->nonextcp )
5515 		sp->prev->from->nextcp = sp->prevcp;
5516 	    sp->me.y = y;
5517 	    SplineRefigure(sp->prev);
5518 	} else {
5519 	    other = SplinePointCreate(sp->me.x,y);
5520 	    sp->nonextcp = true;
5521 	    SplineMake(sp,other,sp->prev->order2);
5522 	    sp = other;
5523 	}
5524     } else {
5525 	if ( sp->nonextcp || y<=sp->me.y ) {
5526 	    sp->nextcp.y += (y-sp->me.y);
5527 	    if ( sp->next->order2 && !sp->next->to->noprevcp )
5528 		sp->next->to->prevcp = sp->nextcp;
5529 	    sp->me.y = y;
5530 	    SplineRefigure(sp->next);
5531 	} else {
5532 	    other = SplinePointCreate(sp->me.x,y);
5533 	    sp->noprevcp = true;
5534 	    SplineMake(other,sp,sp->next->order2);
5535 	    sp = other;
5536 	}
5537     }
5538 return( sp );
5539 }
5540 
StemMoveDTopEndTo(SplinePoint * sp,double y,DStemInfo * d,int is_start)5541 static SplinePoint *StemMoveDTopEndTo(SplinePoint *sp,double y,DStemInfo *d,
5542 	int is_start) {
5543     SplinePoint *other;
5544     double xoff;
5545 
5546     xoff = (y-sp->me.y)*d->unit.x/d->unit.y;
5547     if ( is_start ) {
5548 	other = SplinePointCreate(sp->me.x+xoff,y);
5549 	sp->nonextcp = true;
5550 	SplineMake(sp,other,sp->prev->order2);
5551 	sp = other;
5552     } else {
5553 	other = SplinePointCreate(sp->me.x+xoff,y);
5554 	sp->noprevcp = true;
5555 	SplineMake(other,sp,sp->next->order2);
5556 	sp = other;
5557     }
5558 return( sp );
5559 }
5560 
StemMoveTopEndCarefully(SplinePoint * sp,SplineSet * oldss,SplineSet * ss,DStemInfo * d,int is_start)5561 static SplinePoint *StemMoveTopEndCarefully(SplinePoint *sp,SplineSet *oldss,
5562 	SplineSet *ss,DStemInfo *d, int is_start) {
5563     SplinePoint *other = is_start ? ss->first : ss->last;
5564 
5565     if ( is_start ) {
5566 	if ( sp->me.y>other->me.y &&
5567 		(( sp->noprevcp && other->me.y<sp->prev->from->me.y) ||
5568 		 (!sp->noprevcp && other->me.y<sp->prevcp.y)) ) {
5569 	    /* We need to move sp up, but we can't because it turns down */
5570 	    /*  So instead, move "other" down to sp */
5571 	    extended ts[3];
5572 	    /* Well, we might be able to move it up a little... */
5573 	    if ( sp->prev->from->me.x==sp->me.x ) {
5574 		SplinePoint *newsp = sp->prev->from;
5575 		SplineFree(sp->prev);
5576 		SplinePointFree(sp);
5577 		if ( sp==oldss->first ) {
5578 		    oldss->first = oldss->last = newsp;
5579 		    oldss->start_offset = 0;
5580 		}
5581 		sp=newsp;
5582 	    }
5583 	    CubicSolve(&other->next->splines[1],sp->me.y,ts);
5584 	    if ( ts[0]!=-1 ) {
5585 		SplinePoint *newend = SplineBisect(other->next,ts[0]);
5586 		SplineFree(newend->prev);
5587 		SplinePointFree(other);
5588 		newend->prev = NULL;
5589 		newend->nextcp.x += sp->me.x-newend->me.x;
5590 		if ( newend->next->order2 && !newend->nonextcp )
5591 		    newend->next->to->prevcp = newend->nextcp;
5592 		newend->me.x = sp->me.x;
5593 		ss->first = newend;
5594 		ss->start_offset = 0;
5595 return( sp );
5596 	    }
5597 	}
5598     } else {
5599 	if ( sp->me.y>other->me.y &&
5600 		(( sp->nonextcp && other->me.y<sp->next->to->me.y) ||
5601 		 (!sp->nonextcp && other->me.y<sp->nextcp.y)) ) {
5602 	    extended ts[3];
5603 	    if ( sp->next->to->me.x==sp->me.x ) {
5604 		SplinePoint *newsp = sp->next->to;
5605 		SplineFree(sp->next);
5606 		SplinePointFree(sp);
5607 		if ( sp==oldss->first ) {
5608 		    oldss->first = oldss->last = newsp;
5609 		    oldss->start_offset = 0;
5610 		}
5611 		sp=newsp;
5612 	    }
5613 	    CubicSolve(&other->prev->splines[1],sp->me.y,ts);
5614 	    if ( ts[0]!=-1 ) {
5615 		SplinePoint *newend = SplineBisect(other->prev,ts[0]);
5616 		SplineFree(newend->next);
5617 		SplinePointFree(other);
5618 		newend->next = NULL;
5619 		newend->prevcp.x += sp->me.x-newend->me.x;
5620 		if ( newend->prev->order2 && !newend->noprevcp )
5621 		    newend->prev->from->nextcp = newend->prevcp;
5622 		newend->me.x = sp->me.x;
5623 		ss->last = newend;
5624 return( sp );
5625 	    }
5626 	}
5627     }
5628     if ( d==NULL )
5629 return( StemMoveTopEndTo(sp,other->me.y,is_start));
5630     else
5631 return( StemMoveDTopEndTo(sp,other->me.y,d,is_start));
5632 }
5633 
SplineNextSplice(SplinePoint * start,SplinePoint * newstuff)5634 static void SplineNextSplice(SplinePoint *start,SplinePoint *newstuff) {
5635     start->next = newstuff->next;
5636     start->next->from = start;
5637     start->nextcp = newstuff->nextcp;
5638     start->nonextcp = newstuff->nonextcp;
5639     if ( start->me.x!=newstuff->me.x || start->me.y!=newstuff->me.y ) {
5640 	double xdiff = start->me.x-newstuff->me.x, ydiff = start->me.y-newstuff->me.y;
5641 	SplinePoint *nsp = start->next->to;
5642 	if ( start->next->order2 ) {
5643 	    if ( !nsp->noprevcp ) {
5644 		start->nextcp.x += xdiff/2;
5645 		start->nextcp.y += ydiff/2;
5646 		nsp->prevcp = start->nextcp;
5647 	    } else {
5648 		start->nextcp.x += xdiff;
5649 		start->nextcp.y += ydiff;
5650 	    }
5651 	} else {
5652 	    start->nextcp.x += xdiff;
5653 	    start->nextcp.y += ydiff;
5654 	    nsp->prevcp.x += xdiff/2;
5655 	    nsp->prevcp.y += ydiff/2;
5656 	    nsp->me.x += xdiff/2;
5657 	    nsp->me.y += ydiff/2;
5658 	    nsp->nextcp.x += xdiff/2;
5659 	    nsp->nextcp.y += ydiff/2;
5660 	    SplineRefigure(nsp->next);
5661 	}
5662 	SplineRefigure(nsp->prev);
5663     }
5664     SplinePointFree(newstuff);
5665 }
5666 
SplinePrevSplice(SplinePoint * end,SplinePoint * newstuff)5667 static void SplinePrevSplice(SplinePoint *end,SplinePoint *newstuff) {
5668     end->prev = newstuff->prev;
5669     end->prev->to = end;
5670     end->prevcp = newstuff->prevcp;
5671     end->noprevcp = newstuff->noprevcp;
5672     if ( end->me.x!=newstuff->me.x || end->me.y!=newstuff->me.y ) {
5673 	double xdiff = end->me.x-newstuff->me.x, ydiff = end->me.y-newstuff->me.y;
5674 	SplinePoint *psp = end->prev->from;
5675 	if ( end->prev->order2 ) {
5676 	    if ( !psp->noprevcp ) {
5677 		end->prevcp.x += xdiff/2;
5678 		end->prevcp.y += ydiff/2;
5679 		psp->nextcp = end->prevcp;
5680 	    } else {
5681 		end->nextcp.x += xdiff;
5682 		end->nextcp.y += ydiff;
5683 	    }
5684 	} else {
5685 	    end->nextcp.x += xdiff;
5686 	    end->nextcp.y += ydiff;
5687 	    psp->prevcp.x += xdiff/2;
5688 	    psp->prevcp.y += ydiff/2;
5689 	    psp->me.x += xdiff/2;
5690 	    psp->me.y += ydiff/2;
5691 	    psp->nextcp.x += xdiff/2;
5692 	    psp->nextcp.y += ydiff/2;
5693 	    SplineRefigure(psp->prev);
5694 	}
5695 	SplineRefigure(psp->next);
5696     }
5697     SplinePointFree(newstuff);
5698 }
5699 
ReSerifBottomStem(SplineChar * sc,int layer,StemInfo * h,ItalicInfo * ii)5700 static void ReSerifBottomStem(SplineChar *sc,int layer,StemInfo *h,ItalicInfo *ii) {
5701     SplinePoint *start, *end;
5702     SplineSet *ss, *oldss;
5703 
5704     if ( h==NULL )
5705 return;
5706     FindBottomSerifOnStem(sc,layer,h,0,ii,&start,&end,&oldss);
5707     if ( start==NULL || end==NULL || start==end )
5708 return;
5709     SerifRemove(start,end,oldss);
5710 
5711     ss = MakeBottomItalicSerif(start->me.x-end->me.x,end->me.x,ii,0);
5712     start = StemMoveBottomEndCarefully(start,oldss,ss,NULL,true );
5713     end   = StemMoveBottomEndCarefully(end  ,oldss,ss,NULL,false);
5714 
5715     SplineNextSplice(start,ss->first);
5716     SplinePrevSplice(end,ss->last);
5717     chunkfree(ss,sizeof(*ss));
5718 }
5719 
ReSerifBottomDStem(SplineChar * sc,int layer,DStemInfo * d,ItalicInfo * ii)5720 static void ReSerifBottomDStem(SplineChar *sc,int layer,DStemInfo *d,ItalicInfo *ii) {
5721     SplinePoint *start, *end;
5722     SplineSet *ss, *oldss;
5723     double stemwidth;
5724     int seriftype;
5725 
5726     if ( d==NULL )
5727 return;
5728     FindBottomSerifOnDStem(sc,layer,d,0,ii,&start,&end,&oldss);
5729     if ( start==NULL || end==NULL || start==end )
5730 return;
5731     SerifRemove(start,end,oldss);
5732 
5733     stemwidth = (d->right.x-d->left.x)*d->unit.y - (d->right.y-d->left.y)*d->unit.x;
5734     if ( stemwidth<0 ) stemwidth = -stemwidth;
5735     if ( d->unit.x*d->unit.y<0 )
5736 	seriftype = 0;
5737     else if ( stemwidth<boldleftfacingitalicserif.stemwidth*ii->emsize/1000.0+5 )
5738 	seriftype = 1;
5739     else
5740 	seriftype = 0;
5741     ss = MakeItalicDSerif(d,stemwidth, end->me.x,ii,seriftype,false);
5742 
5743     start = StemMoveBottomEndCarefully(start,oldss,ss,d,true );
5744     end   = StemMoveBottomEndCarefully(end  ,oldss,ss,d,false);
5745 
5746     SplineNextSplice(start,ss->first);
5747     SplinePrevSplice(end,ss->last);
5748     chunkfree(ss,sizeof(*ss));
5749 }
5750 
ReSerifXHeightDStem(SplineChar * sc,int layer,DStemInfo * d,ItalicInfo * ii)5751 static void ReSerifXHeightDStem(SplineChar *sc,int layer,DStemInfo *d,ItalicInfo *ii) {
5752     SplinePoint *start, *end;
5753     SplineSet *ss, *oldss;
5754     double stemwidth;
5755     int seriftype;
5756 
5757     if ( d==NULL )
5758 return;
5759     FindTopSerifOnDStem(sc,layer,d,ii->x_height,ii,&start,&end,&oldss);
5760     if ( start==NULL || end==NULL || start==end )
5761 return;
5762     SerifRemove(start,end,oldss);
5763 
5764     stemwidth = (d->right.x-d->left.x)*d->unit.y - (d->right.y-d->left.y)*d->unit.x;
5765     if ( stemwidth<0 ) stemwidth = -stemwidth;
5766     if ( d->unit.x*d->unit.y<0 )
5767 	seriftype = 0;
5768     else if ( stemwidth<boldleftfacingitalicserif.stemwidth*ii->emsize/1000.0+5 )
5769 	seriftype = 1;
5770     else
5771 	seriftype = 0;
5772     ss = MakeItalicDSerif(d,stemwidth, end->me.x,ii,seriftype,1);
5773 
5774     start = StemMoveTopEndCarefully(start,oldss,ss,d,true );
5775     end   = StemMoveTopEndCarefully(end  ,oldss,ss,d,false);
5776 
5777     SplineNextSplice(start,ss->first);
5778     SplinePrevSplice(end,ss->last);
5779     chunkfree(ss,sizeof(*ss));
5780 }
5781 
NearBottomRightSide(DStemInfo * d,DBounds * b,ItalicInfo * ii)5782 static int NearBottomRightSide(DStemInfo *d,DBounds *b,ItalicInfo *ii) {
5783     double x;
5784 
5785     x = d->left.x - d->left.y*d->unit.x/d->unit.y;
5786     if ( x+1.5*ii->serif_extent+30>b->maxx )
5787 return( true );
5788     x = d->right.x - d->right.y*d->unit.x/d->unit.y;
5789     if ( x+1.5*ii->serif_extent+30>b->maxx )
5790 return( true );
5791 
5792 return( false );
5793 }
5794 
NearBottomLeftSide(DStemInfo * d,DBounds * b,ItalicInfo * ii)5795 static int NearBottomLeftSide(DStemInfo *d,DBounds *b,ItalicInfo *ii) {
5796     double x;
5797 
5798     x = d->left.x - d->left.y*d->unit.x/d->unit.y;
5799     if ( x-1.5*ii->serif_extent-30<b->minx )
5800 return( true );
5801     x = d->right.x - d->right.y*d->unit.x/d->unit.y;
5802     if ( x-1.5*ii->serif_extent-30<b->minx )
5803 return( true );
5804 
5805 return( false );
5806 }
5807 
NearXHeightRightSide(DStemInfo * d,DBounds * b,ItalicInfo * ii)5808 static int NearXHeightRightSide(DStemInfo *d,DBounds *b,ItalicInfo *ii) {
5809     double x;
5810 
5811     x = d->left.x - (d->left.y-ii->x_height)*d->unit.x/d->unit.y;
5812     if ( x+1.5*ii->serif_extent+30>b->maxx )
5813 return( true );
5814     x = d->right.x - (d->right.y-ii->x_height)*d->unit.x/d->unit.y;
5815     if ( x+1.5*ii->serif_extent+30>b->maxx )
5816 return( true );
5817 
5818 return( false );
5819 }
5820 
NearXHeightLeftSide(DStemInfo * d,DBounds * b,ItalicInfo * ii)5821 static int NearXHeightLeftSide(DStemInfo *d,DBounds *b,ItalicInfo *ii) {
5822     double x;
5823 
5824     x = d->left.x - (d->left.y-ii->x_height)*d->unit.x/d->unit.y;
5825     if ( x-1.5*ii->serif_extent-30<b->minx )
5826 return( true );
5827     x = d->right.x - (d->right.y-ii->x_height)*d->unit.x/d->unit.y;
5828     if ( x-1.5*ii->serif_extent-30<b->minx )
5829 return( true );
5830 
5831 return( false );
5832 }
5833 
AddBottomItalicSerifs(SplineChar * sc,int layer,ItalicInfo * ii)5834 static void AddBottomItalicSerifs(SplineChar *sc,int layer,ItalicInfo *ii) {
5835     StemInfo *h;
5836     int j, cnt;
5837     /* If a glyph has multiple stems then only the last one gets turned to */
5838     /*  the italic serif. The others get deserifed. Note that if the glyph is */
5839     /*  "k" then the "last stem" is actually a diagonal stem rather than a */
5840     /*  vertical one */
5841     /* "r", on the other hand, just doesn't get a serif */
5842     /* Hmm better than saying the last stem, let's say stem at the right edge */
5843     DBounds b;
5844 
5845     SplineCharLayerFindBounds(sc,layer,&b);
5846     for ( h=sc->vstem, cnt=0; h!=NULL; h=h->next )
5847 	if ( h->tobeused )
5848 	    ++cnt;
5849     for ( j=0, h=sc->vstem; h!=NULL; h=h->next ) if ( h->tobeused ) {
5850 	if ( j==cnt-1 && h->start+h->width+ii->serif_extent+20>b.maxx )
5851 	    ReSerifBottomStem(sc,layer,h,ii);
5852 	else
5853 	    DeSerifBottomStem(sc,layer,h,ii,0,NULL,NULL);
5854 	++j;
5855     }
5856 }
5857 
DeSerifTopStem(SplineChar * sc,int layer,StemInfo * h,ItalicInfo * ii,double y)5858 static void DeSerifTopStem(SplineChar *sc,int layer,StemInfo *h,ItalicInfo *ii,
5859 	double y) {
5860     SplinePoint *start, *end;
5861     SplineSet *ss;
5862 
5863     if ( h==NULL )
5864 return;
5865     FindTopSerifOnStem(sc,layer,h,y,ii,&start,&end,&ss);
5866     if ( start==NULL || end==NULL || start==end )
5867 return;
5868     SerifRemove(start,end,ss);
5869 
5870     start = StemMoveBottomEndTo(start,y,true);
5871     end = StemMoveBottomEndTo(end,y,false);
5872     start->nonextcp = end->noprevcp = true;
5873     SplineMake(start,end,sc->layers[layer].order2);
5874     start->pointtype = end->pointtype = pt_corner;
5875 }
5876 
ReSerifTopStem(SplineChar * sc,int layer,StemInfo * h,ItalicInfo * ii)5877 static void ReSerifTopStem(SplineChar *sc,int layer,StemInfo *h,ItalicInfo *ii) {
5878     SplinePoint *start=NULL, *end=NULL;
5879     SplineSet *ss, *oldss;
5880     int at_xh = true;
5881 
5882     if ( h==NULL )
5883 return;
5884     if ( ii->transform_top_xh_serifs )
5885 	FindTopSerifOnStem(sc,layer,h,ii->x_height,ii,&start,&end,&oldss);
5886     if ( start==NULL && ii->transform_top_as_serifs ) {
5887 	FindTopSerifOnStem(sc,layer,h,ii->ascender_height,ii,&start,&end,&oldss);
5888 	at_xh = false;
5889     }
5890     if ( start==NULL || end==NULL || start==end || !IsLeftHalfSerif(start,end,h))
5891 return;
5892     SerifRemove(start,end,oldss);
5893 
5894     ss = MakeTopItalicSerif(start->me.x-end->me.x,end->me.x,ii,at_xh);
5895     start = StemMoveTopEndCarefully(start,oldss,ss,NULL,true );
5896     end   = StemMoveTopEndCarefully(end  ,oldss,ss,NULL,false);
5897 
5898     SplineNextSplice(start,ss->first);
5899     SplinePrevSplice(end,ss->last);
5900     chunkfree(ss,sizeof(*ss));
5901 }
5902 
AddTopItalicSerifs(SplineChar * sc,int layer,ItalicInfo * ii)5903 static void AddTopItalicSerifs(SplineChar *sc,int layer,ItalicInfo *ii) {
5904     StemInfo *h;
5905     /* The leftmost stem of certain lower case letters can go from a half */
5906     /* serif to an italic serif. Sometimes it is just stems at the x-height */
5907     /* sometimes ascenders too, often none */
5908     DBounds b;
5909 
5910     SplineCharLayerFindBounds(sc,layer,&b);
5911     for ( h=sc->vstem; h!=NULL; h=h->next ) if ( h->tobeused ) {
5912 	if ( h->start-ii->serif_extent-20<b.minx || sc->unicodeenc=='j' || sc->unicodeenc==0x237 )
5913 	    ReSerifTopStem(sc,layer,h,ii);
5914     break;
5915     }
5916     if ( sc->unicodeenc=='u' || sc->unicodeenc==0x436 ) {
5917 	if ( h!=NULL && sc->unicodeenc=='u' )
5918 	    h=h->next;
5919 	while ( h!=NULL ) {
5920 	    if ( h->tobeused ) {
5921 		DeSerifTopStem(sc,layer,h,ii,ii->x_height);
5922 	break;
5923 	    }
5924 	    h = h->next;
5925 	}
5926     }
5927 }
5928 
AddDiagonalItalicSerifs(SplineChar * sc,int layer,ItalicInfo * ii)5929 static void AddDiagonalItalicSerifs(SplineChar *sc,int layer,ItalicInfo *ii) {
5930     DStemInfo *d;
5931     DBounds b;
5932 
5933     SplineCharLayerFindBounds(sc,layer,&b);
5934     for ( d=sc->dstem; d!=NULL; d=d->next ) {
5935       /* Serifs on diagonal stems at baseline */
5936 	if ( d->unit.x*d->unit.y<0 && NearBottomRightSide(d,&b,ii))
5937 	    ReSerifBottomDStem(sc,layer,d,ii);
5938 	else if ( d->unit.x*d->unit.y>0 && NearBottomLeftSide(d,&b,ii))
5939 	    ReSerifBottomDStem(sc,layer,d,ii);
5940 
5941       /* Serifs on diagonal stems at xheight */
5942 	if ( d->unit.x*d->unit.y>0 && NearXHeightRightSide(d,&b,ii))
5943 	    ReSerifXHeightDStem(sc,layer,d,ii);
5944 	else if ( d->unit.x*d->unit.y<0 && NearXHeightLeftSide(d,&b,ii))
5945 	    ReSerifXHeightDStem(sc,layer,d,ii);
5946     }
5947 }
5948 
FFCopyTrans(ItalicInfo * ii,real * transform,SplinePoint ** ff_start1,SplinePoint ** ff_end1,SplinePoint ** ff_start2,SplinePoint ** ff_end2)5949 static int FFCopyTrans(ItalicInfo *ii,real *transform,
5950 	SplinePoint **ff_start1,SplinePoint **ff_end1, SplinePoint **ff_start2, SplinePoint **ff_end2) {
5951     SplinePoint *sp, *last, *cur;
5952     int touches;
5953 
5954     last = NULL;
5955     for ( sp = ii->ff_start1; ; sp=sp->next->to ) {
5956 	cur = chunkalloc(sizeof(SplinePoint));
5957 	*cur = *sp;
5958 	cur->hintmask = NULL;
5959 	cur->me.x = transform[0]*sp->me.x + transform[2]*sp->me.y + transform[4];
5960 	cur->me.y = transform[1]*sp->me.x + transform[3]*sp->me.y + transform[5];
5961 	cur->nextcp.x = transform[0]*sp->nextcp.x + transform[2]*sp->nextcp.y + transform[4];
5962 	cur->nextcp.y = transform[1]*sp->nextcp.x + transform[3]*sp->nextcp.y + transform[5];
5963 	cur->prevcp.x = transform[0]*sp->prevcp.x + transform[2]*sp->prevcp.y + transform[4];
5964 	cur->prevcp.y = transform[1]*sp->prevcp.x + transform[3]*sp->prevcp.y + transform[5];
5965 	if ( last==NULL )
5966 	    *ff_start1 = cur;
5967 	else
5968 	    SplineMake(last,cur,sp->prev->order2);
5969 	last = cur;
5970 	if ( sp==ii->ff_end1 || sp==ii->ff_end2 )
5971     break;
5972     }
5973     if ( sp==ii->ff_end1 ) {
5974 	touches = false;
5975 	*ff_end1 = last;
5976     } else {
5977 	touches = true;
5978 	*ff_end2 = last;
5979     }
5980 
5981     last = NULL;
5982     for ( sp = ii->ff_start2; ; sp=sp->next->to ) {
5983 	cur = chunkalloc(sizeof(SplinePoint));
5984 	*cur = *sp;
5985 	cur->hintmask = NULL;
5986 	cur->me.x = transform[0]*sp->me.x + transform[2]*sp->me.y + transform[4];
5987 	cur->me.y = transform[1]*sp->me.x + transform[3]*sp->me.y + transform[5];
5988 	cur->nextcp.x = transform[0]*sp->nextcp.x + transform[2]*sp->nextcp.y + transform[4];
5989 	cur->nextcp.y = transform[1]*sp->nextcp.x + transform[3]*sp->nextcp.y + transform[5];
5990 	cur->prevcp.x = transform[0]*sp->prevcp.x + transform[2]*sp->prevcp.y + transform[4];
5991 	cur->prevcp.y = transform[1]*sp->prevcp.x + transform[3]*sp->prevcp.y + transform[5];
5992 	if ( last==NULL )
5993 	    *ff_start2 = cur;
5994 	else
5995 	    SplineMake(last,cur,sp->prev->order2);
5996 	last = cur;
5997 	if ( sp==ii->ff_end1 || sp==ii->ff_end2 )
5998     break;
5999     }
6000     if ( sp==ii->ff_end1 ) {
6001 	*ff_end1 = last;
6002     } else {
6003 	*ff_end2 = last;
6004     }
6005 return( touches );
6006 }
6007 
FigureFFTop(ItalicInfo * ii)6008 static void FigureFFTop(ItalicInfo *ii) {
6009     SplineChar *ff;
6010     StemInfo *h;
6011     SplinePoint *bests[2][2], *sp;
6012     SplineSet *ss;
6013     double sdiff, ediff;
6014     DBounds b;
6015     int i;
6016     real trans[6];
6017 
6018     if ( ii->ff_start1!=NULL )
6019 return;
6020 
6021     ff = SFGetChar(ii->sf,0xfb00,NULL);			/* unicode ff ligature */
6022     if ( ff==NULL )
6023 	ff = SFGetChar(ii->sf,-1, "f_f");
6024     if ( ff==NULL )
6025 	ff = SFGetChar(ii->sf,-1, "longs_longs");		/* Long s */
6026     if ( ff==NULL )
6027 return;
6028     if ( autohint_before_generate && (ff->changedsincelasthinted || ff->vstem==NULL) &&
6029 	    !ff->manualhints )
6030 	SplineCharAutoHint(ff,ii->layer,NULL);
6031     FigureGoodStems(ff->vstem);
6032 
6033     memset(bests,0,sizeof(bests));
6034     for ( ss=ff->layers[ii->layer].splines; ss!=NULL; ss=ss->next ) {
6035 	if ( ss->first->prev==NULL )
6036     continue;
6037 	for ( sp=ss->first; ; ) {
6038 	    if ( sp->me.y>.9*ii->x_height ) {
6039 		for ( h=ff->vstem, i=0; h!=NULL && i<2; h=h->next ) if ( h->tobeused ) {
6040 		    if ( (sdiff = sp->me.x-h->start)<0 ) sdiff = -sdiff;
6041 		    if ( (ediff = sp->me.x-h->start-h->width)<0 ) ediff = -ediff;
6042 		    if ( sdiff<3 && (bests[i][0]==NULL || sp->me.y>bests[i][0]->me.y )) {
6043 			bests[i][0] = sp;
6044 		    } else if ( ediff<3 && (bests[i][1]==NULL || sp->me.y>bests[i][1]->me.y )) {
6045 			bests[i][1] = sp;
6046 		    }
6047 		    ++i;
6048 		}
6049 	    }
6050 	    sp = sp->next->to;
6051 	    if ( sp==ss->first )
6052 	break;
6053 	}
6054     }
6055 
6056     if ( bests[0][0]==NULL || bests[0][1]==NULL || bests[1][0]==NULL || bests[1][1]==NULL )
6057 return;
6058 
6059     ii->ff_start1 = bests[0][0]; ii->ff_end1 = bests[0][1];
6060     ii->ff_start2 = bests[1][0]; ii->ff_end2 = bests[1][1];
6061     SplineCharLayerFindBounds(ff,ii->layer,&b);
6062     ii->ff_height = b.maxy - ii->ff_start1->me.y;
6063 
6064     /* Now we need to save a copy of this because we might modify the ff glyph */
6065     /*  before we do the "ffi" glyph (we will if things are in unicode ordering) */
6066     /*  and then we'd try to put a pre-slanted thing on the bottom of ffi and */
6067     /*  cause all kinds of havoc */
6068     memset(trans,0,sizeof(trans));
6069     trans[0] = trans[3] = 1;
6070     FFCopyTrans(ii,trans,&bests[0][0],&bests[0][1],&bests[1][0],&bests[1][1]);
6071     ii->ff_start1 = bests[0][0]; ii->ff_end1 = bests[0][1];
6072     ii->ff_start2 = bests[1][0]; ii->ff_end2 = bests[1][1];
6073 }
6074 
FFBottomFromTop(SplineChar * sc,int layer,ItalicInfo * ii)6075 static void FFBottomFromTop(SplineChar *sc,int layer,ItalicInfo *ii) {
6076     StemInfo *h;
6077     SplinePoint *start[2], *end[2], *f_start[2], *f_end[2];
6078     SplineSet *ss[2];
6079     real transform[6];
6080     int cnt;
6081     double bottom_y = 0;
6082     int touches;
6083 
6084     FigureFFTop(ii);
6085     if ( ii->ff_start1==NULL )
6086 return;
6087     cnt=0;
6088     for ( h=sc->vstem; h!=NULL && cnt<2; h=h->next ) {
6089 	if ( !h->tobeused )
6090     continue;
6091 	FindBottomSerifOnStem(sc,layer,h,bottom_y,ii,&start[cnt],&end[cnt],&ss[cnt]);
6092 	if ( start[cnt]==NULL )
6093     continue;
6094 	++cnt;
6095     }
6096 
6097     if ( cnt!=2 )
6098 return;
6099 
6100     SerifRemove(start[0],end[0],ss[0]);
6101     SerifRemove(start[1],end[1],ss[1]);
6102 
6103     memset(transform,0,sizeof(transform));
6104     transform[0] = transform[3] = -1;
6105     transform[4] = start[0]->me.x + ii->ff_start2->me.x;
6106     transform[5] = ii->ff_start1->me.y + ii->pq_depth + ii->ff_height;
6107     touches = FFCopyTrans(ii,transform,&f_start[0],&f_end[0],&f_start[1],&f_end[1]);
6108     /* Now there are several cases to consider */
6109     /* First: Did the tops of the two "f"s touch one another or were they two */
6110     /*  distinct stems with no connection. If they touch then start1->end2 is */
6111     /*  one path and start2->end1 (counterclockwise) is the other. If they do */
6112     /*  not touch then start1->end1 and start2->end2. */
6113     /* If they do not touch then things are pretty simple. We remove serifs  */
6114     /*  with one shape from the bottom of two stems and we replace them with */
6115     /*  serifs of a different shape, but the contour structure does not change*/
6116     /* But if they touch... things are more complicated. And we have more cases*/
6117     /* Suppose we are applying things to two "longs"es. Do the two longses */
6118     /*  touch (forming one contour) or are they distinct (forming two) */
6119     /* If they formed two contours then we will join them with our touching */
6120     /*  f-tails and we will need to remove one of the two SplineSet structs */
6121     /* If they form a single contour then we will change that single contour */
6122     /*  into two, an inner and and outer contour, and we need to add an SS */
6123     /*  structure. */
6124     start[0] = StemMoveBottomEndTo(start[0],f_start[1]->me.y,true);
6125     end[0] = StemMoveBottomEndTo(end[0],f_end[1]->me.y,false);
6126     SplineNextSplice(start[0],f_start[1]);
6127     SplinePrevSplice(end[0],f_end[1]);
6128     start[1] = StemMoveBottomEndTo(start[1],f_start[0]->me.y,true);
6129     end[1] = StemMoveBottomEndTo(end[1],f_end[0]->me.y,false);
6130     SplineNextSplice(start[1],f_start[0]);
6131     SplinePrevSplice(end[1],f_end[0]);
6132     if ( touches ) {
6133 	if ( ss[0]==ss[1] ) {
6134 	    ss[0]->first = ss[0]->last = start[0];
6135 	    ss[0]->start_offset = 0;
6136 	    ss[1] = chunkalloc(sizeof(SplineSet));
6137 	    ss[1]->next = ss[0]->next;
6138 	    ss[0]->next = ss[1];
6139 	    ss[1]->first = ss[1]->last = start[1];
6140 	    ss[1]->start_offset = 0;
6141 	} else {
6142 	    SplineSet *spl, *prev;
6143 	    for ( prev=NULL, spl=sc->layers[layer].splines; spl!=ss[1]; spl=spl->next )
6144 		prev = spl;
6145 	    if ( prev==NULL )
6146 		sc->layers[layer].splines = ss[1]->next;
6147 	    else
6148 		prev->next = ss[1]->next;
6149 	    chunkfree(ss[1],sizeof(SplineSet));
6150 	}
6151     }
6152 
6153     SplineSetRefigure(sc->layers[layer].splines);
6154 }
6155 
FCopyTrans(ItalicInfo * ii,real * transform,SplinePoint ** f_start,SplinePoint ** f_end)6156 static void FCopyTrans(ItalicInfo *ii,real *transform,SplinePoint **f_start,SplinePoint **f_end) {
6157     SplinePoint *sp, *last, *cur;
6158 
6159     last = NULL;
6160     for ( sp = ii->f_start; ; sp=sp->next->to ) {
6161 	cur = chunkalloc(sizeof(SplinePoint));
6162 	*cur = *sp;
6163 	cur->hintmask = NULL;
6164 	cur->me.x = transform[0]*sp->me.x + transform[2]*sp->me.y + transform[4];
6165 	cur->me.y = transform[1]*sp->me.x + transform[3]*sp->me.y + transform[5];
6166 	cur->nextcp.x = transform[0]*sp->nextcp.x + transform[2]*sp->nextcp.y + transform[4];
6167 	cur->nextcp.y = transform[1]*sp->nextcp.x + transform[3]*sp->nextcp.y + transform[5];
6168 	cur->prevcp.x = transform[0]*sp->prevcp.x + transform[2]*sp->prevcp.y + transform[4];
6169 	cur->prevcp.y = transform[1]*sp->prevcp.x + transform[3]*sp->prevcp.y + transform[5];
6170 	if ( last==NULL )
6171 	    *f_start = cur;
6172 	else
6173 	    SplineMake(last,cur,sp->prev->order2);
6174 	last = cur;
6175 	if ( sp==ii->f_end )
6176     break;
6177     }
6178     *f_end = last;
6179 }
6180 
FigureFTop(ItalicInfo * ii)6181 static void FigureFTop(ItalicInfo *ii) {
6182     SplineChar *f;
6183     StemInfo *h;
6184     SplinePoint *beste, *bests, *sp;
6185     SplineSet *ss;
6186     double bestediff, sdiff, ediff;
6187     DBounds b;
6188     real trans[6];
6189 
6190     if ( ii->f_start!=NULL )
6191 return;
6192 
6193     f = SFGetChar(ii->sf,'f',NULL);
6194     if ( f==NULL )
6195 	f = SFGetChar(ii->sf,0x17f,NULL);		/* Long s */
6196     if ( f==NULL )
6197 return;
6198     if ( autohint_before_generate && (f->changedsincelasthinted || f->vstem==NULL) &&
6199 	    !f->manualhints )
6200 	SplineCharAutoHint(f,ii->layer,NULL);
6201     FigureGoodStems(f->vstem);
6202 
6203     for ( h=f->vstem; h!=NULL; h=h->next ) if ( h->tobeused ) {
6204 	for ( ss=f->layers[ii->layer].splines; ss!=NULL; ss=ss->next ) {
6205 	    bests = beste = NULL;
6206 	    bestediff = 10;
6207 	    for ( sp=ss->first; ; ) {
6208 		if ( sp->me.y>.9*ii->x_height ) {
6209 		    if ( (sdiff = sp->me.x-h->start)<0 ) sdiff = -sdiff;
6210 		    if ( (ediff = sp->me.x-h->start-h->width)<0 ) ediff = -ediff;
6211 		    if ( sdiff<3 && (bests==NULL || sp->me.y>bests->me.y ))
6212 			bests = sp;
6213 		    else if ( ediff<3 && (beste==NULL || sp->me.y>beste->me.y )) {
6214 			bestediff = ediff;
6215 			beste = sp;
6216 		    }
6217 		}
6218 		if ( sp->next==NULL ) {
6219 		    bests = beste = NULL;
6220 	    break;
6221 		}
6222 		sp = sp->next->to;
6223 		if ( sp==ss->first )
6224 	    break;
6225 	    }
6226 	    if ( bests!=NULL && beste!=NULL ) {
6227 		if ( bests->next->to->me.y > bests->me.y ) {
6228 		    ii->f_start = bests;
6229 		    ii->f_end   = beste;
6230 		} else {
6231 		    ii->f_start = beste;
6232 		    ii->f_end   = bests;
6233 		}
6234 		SplineCharLayerFindBounds(f,ii->layer,&b);
6235 		ii->f_height = b.maxy - ii->f_start->me.y;
6236     /* Now we need to save a copy of this because we might modify the f glyph */
6237     /*  before we do the "fi" glyph (we will if things are in unicode ordering) */
6238     /*  and then we'd try to put a pre-slanted thing on the bottom of fi and */
6239     /*  cause all kinds of havoc */
6240 		memset(trans,0,sizeof(trans));
6241 		trans[0] = trans[3] = 1;
6242 		FCopyTrans(ii,trans,&ii->f_start,&ii->f_end);
6243 return;
6244 	    }
6245 	}
6246     }
6247 }
6248 
FBottomFromTop(SplineChar * sc,int layer,ItalicInfo * ii)6249 static void FBottomFromTop(SplineChar *sc,int layer,ItalicInfo *ii) {
6250     StemInfo *h;
6251     SplinePoint *start, *end, *f_start, *f_end;
6252     SplineSet *ss;
6253     real transform[6];
6254     int cnt;
6255     double bottom_y = 0;
6256     DBounds b;
6257 
6258     if ( sc->unicodeenc==0x444 ) {
6259 	cnt = 1;	/* cyrillic phi is only sometimes like an f. If we get here it is */
6260 	SplineCharLayerFindBounds(sc,layer,&b);
6261 	bottom_y = b.miny;
6262     } else
6263 	cnt = LikeAnF(sc);
6264     if ( cnt==2 ) {
6265 	FFBottomFromTop(sc,layer,ii);
6266 return;
6267     } else if ( cnt!=1 )
6268 return;		/* I don't think the combination fff ever happens except in hex dumps */
6269     FigureFTop(ii);
6270     if ( ii->f_start==NULL || ii->f_end==NULL )
6271 return;
6272     for ( h=sc->vstem; h!=NULL; h=h->next ) {
6273 	if ( !h->tobeused )
6274     continue;
6275 	FindBottomSerifOnStem(sc,layer,h,bottom_y,ii,&start,&end,&ss);
6276 	if ( start==NULL )
6277     continue;
6278 	SerifRemove(start,end,ss);
6279 	memset(transform,0,sizeof(transform));
6280 	transform[0] = transform[3] = -1;
6281 	transform[4] = start->me.x + ii->f_start->me.x;
6282 	transform[5] = ii->f_start->me.y + ii->pq_depth + ii->f_height;
6283 	FCopyTrans(ii,transform,&f_start,&f_end);
6284 	start = StemMoveBottomEndTo(start,f_start->me.y,true);
6285 	end = StemMoveBottomEndTo(end,f_end->me.y,false);
6286 	SplineNextSplice(start,f_start);
6287 	SplinePrevSplice(end,f_end);
6288     break;
6289     }
6290 }
6291 
FBottomGrows(SplineChar * sc,int layer,ItalicInfo * ii)6292 static void FBottomGrows(SplineChar *sc,int layer,ItalicInfo *ii) {
6293     int cnt;
6294     int i;
6295     StemInfo *h;
6296     SplinePoint *start, *end, *sp;
6297 
6298     if ( sc->unicodeenc==0x444 )
6299 	cnt = 1;	/* cyrillic phi is only sometimes like an f. If we get here it is */
6300     else
6301 	cnt = LikeAnF(sc);
6302 
6303     for ( h=sc->vstem, i=0; i<cnt && h!=NULL; h=h->next ) {
6304 	if ( !h->tobeused )
6305     continue;
6306 	DeSerifBottomStem(sc,layer,h,ii,0,&start,&end);
6307 	if ( start!=NULL ) {
6308 	    /* The stem currently ends at the baseline, move it down to the */
6309 	    /*  pq_descent line */
6310 	    if ( !start->noprevcp ) {
6311 		sp = SplinePointCreate(start->me.x,start->me.y+ii->pq_depth);
6312 		sp->next = start->next;
6313 		sp->next->from = sp;
6314 		start->nonextcp = true;
6315 		SplineMake(start,sp,sc->layers[layer].order2);
6316 		start = sp;
6317 	    } else {
6318 		start->me.y = start->nextcp.y = start->prevcp.y += ii->pq_depth;
6319 		SplineRefigure(start->prev);
6320 	    }
6321 	    if ( !end->nonextcp ) {
6322 		sp = SplinePointCreate(end->me.x,end->me.y+ii->pq_depth);
6323 		sp->prev = start->prev;
6324 		sp->prev->to = sp;
6325 		end->noprevcp = true;
6326 		SplineMake(sp,end,sc->layers[layer].order2);
6327 		end = sp;
6328 	    } else {
6329 		end->me.y = end->nextcp.y = end->prevcp.y += ii->pq_depth;
6330 		SplineRefigure(end->next);
6331 	    }
6332 	    if ( start->next->to == end )
6333 		SplineRefigure(start->next);
6334 	    else {
6335 		SplinePoint *mid = start->next->to;
6336 		mid->me.y = mid->nextcp.y = mid->prevcp.y += ii->pq_depth;
6337 		SplineRefigure(mid->next);
6338 		SplineRefigure(mid->prev);
6339 	    }
6340 	    ++i;
6341 	}
6342     }
6343 }
6344 
FBottomTransform(SplineChar * sc,int layer,ItalicInfo * ii)6345 static void FBottomTransform(SplineChar *sc,int layer,ItalicInfo *ii) {
6346     if ( ii->f_rotate_top )
6347 	FBottomFromTop(sc,layer,ii);
6348     else
6349 	FBottomGrows(sc,layer,ii);
6350 }
6351 
CyrilicPhiTop(SplineChar * sc,int layer,ItalicInfo * ii)6352 static void CyrilicPhiTop(SplineChar *sc,int layer,ItalicInfo *ii) {
6353     StemInfo *h;
6354     SplinePoint *start, *end, *f_start, *f_end;
6355     SplineSet *ss;
6356     real transform[6];
6357     DBounds b;
6358 
6359     FigureFTop(ii);
6360     if ( ii->f_start==NULL || ii->f_end==NULL )
6361 return;
6362     SplineCharLayerFindBounds(sc,layer,&b);
6363     for ( h=sc->vstem; h!=NULL; h=h->next ) {
6364 	if ( !h->tobeused )
6365     continue;
6366 	FindTopSerifOnStem(sc,layer,h,b.maxy,ii,&start,&end,&ss);
6367 	if ( start==NULL )
6368     continue;
6369 	SerifRemove(start,end,ss);
6370 	memset(transform,0,sizeof(transform));
6371 	transform[0] = transform[3] = 1;
6372 	transform[4] = start->me.x - ii->f_start->me.x;
6373 	FCopyTrans(ii,transform,&f_start,&f_end);
6374 	start = StemMoveTopEndTo(start,f_start->me.y,true);
6375 	end = StemMoveTopEndTo(end,f_end->me.y,false);
6376 	SplineNextSplice(start,f_start);
6377 	SplinePrevSplice(end,f_end);
6378     break;
6379     }
6380 }
6381 
ItalReplaceWithReferenceTo(SplineChar * sc,int layer,int uni)6382 static void ItalReplaceWithReferenceTo(SplineChar *sc,int layer, int uni) {
6383     SplineChar *replacement = SFGetChar(sc->parent,uni,NULL);
6384     RefChar *newref;
6385 
6386     if ( replacement==NULL )
6387 return;
6388     SCClearLayer(sc,layer);
6389 
6390     sc->width = replacement->width;
6391 
6392     newref = RefCharCreate();
6393     free(newref->layers);
6394     newref->transform[0] = newref->transform[3] = 1;
6395     newref->layers = NULL;
6396     newref->layer_cnt = 0;
6397     newref->sc = replacement;
6398     sc->layers[layer].refs = newref;
6399     SCReinstanciateRefChar(sc,newref,layer);
6400     SCMakeDependent(sc,replacement);
6401 }
6402 
ItalReplaceWithRotated(SplineChar * sc,int layer,int uni,ItalicInfo * ii)6403 static void ItalReplaceWithRotated(SplineChar *sc,int layer, int uni, ItalicInfo *ii) {
6404     SplineChar *replacement = SFGetChar(sc->parent,uni,NULL);
6405     RefChar *newref;
6406     DBounds b;
6407 
6408     if ( replacement==NULL )
6409 return;
6410     SplineCharLayerFindBounds(replacement,layer,&b);
6411 
6412     SCClearLayer(sc,layer);
6413 
6414     sc->width = replacement->width;
6415 
6416     newref = RefCharCreate();
6417     free(newref->layers);
6418     newref->transform[0] = newref->transform[3] = -1;
6419     newref->transform[4] = replacement->width;
6420     /* I want the old xheight to be at the baseline */
6421     newref->transform[5] = ii->x_height;
6422     newref->layers = NULL;
6423     newref->layer_cnt = 0;
6424     newref->sc = replacement;
6425     sc->layers[layer].refs = newref;
6426     SCReinstanciateRefChar(sc,newref,layer);
6427     SCMakeDependent(sc,replacement);
6428 }
6429 
ItalReplaceWithSmallCaps(SplineChar * sc,int layer,int uni,ItalicInfo * ii)6430 static void ItalReplaceWithSmallCaps(SplineChar *sc,int layer, int uni, ItalicInfo *ii) {
6431     SplineChar *uc = SFGetChar(sc->parent,uni,NULL);
6432     struct smallcaps small;
6433     struct genericchange genchange;
6434 
6435     if ( uc==NULL )
6436 return;
6437 
6438     SmallCapsFindConstants(&small, sc->parent, layer);
6439     if ( uc->ticked ) {
6440 	small.italic_angle = ii->italic_angle;
6441 	small.tan_ia       = ii->tan_ia;
6442     }
6443 
6444     SCClearLayer(sc,layer);
6445 
6446     memset(&genchange,0,sizeof(genchange));
6447     genchange.small = &small;
6448     genchange.gc = gc_smallcaps;
6449     genchange.extension_for_letters = "sc";
6450     genchange.extension_for_symbols = "taboldstyle";
6451 
6452     genchange.stem_width_scale  = small.lc_stem_width / small.uc_stem_width;
6453     genchange.stem_height_scale = genchange.stem_width_scale;
6454     genchange.v_scale	   = small.xheight / small.capheight;
6455     genchange.hcounter_scale    = genchange.v_scale;
6456     genchange.lsb_scale = genchange.rsb_scale = genchange.v_scale;
6457 
6458     ChangeGlyph( sc,uc,layer,&genchange );
6459 }
6460 
Ital_a_From_d(SplineChar * sc,int layer,ItalicInfo * ii)6461 static void Ital_a_From_d(SplineChar *sc,int layer, ItalicInfo *ii) {
6462     SplineChar *d = SFGetChar(sc->parent,'d',NULL);
6463     SplineSet *d_ss, *ss, *sses[4];
6464     Spline *s, *first, *splines[4], *left, *right;
6465     SplinePoint *start, *end, *ltemp, *rtemp;
6466     int scnt, left_is_start;
6467     double stemwidth, drop, min;
6468     extended ts[3];
6469 
6470     if ( d==NULL )
6471 return;
6472     d_ss = SplinePointListCopy(LayerAllSplines(&d->layers[layer]));
6473     LayerUnAllSplines(&d->layers[ly_fore]);
6474 
6475     if ( d->ticked ) {
6476 	real deskew[6];
6477 	memset(deskew,0,sizeof(deskew));
6478 	deskew[0] = deskew[3] = 1;
6479 	deskew[2] = ii->tan_ia;
6480 	SplinePointListTransform(d_ss,deskew,tpt_AllPoints);
6481     }
6482 
6483     scnt = 0;
6484     for ( ss=d_ss; ss!=NULL; ss=ss->next ) {
6485 	first = NULL;
6486 	for ( s=ss->first->next; s!=NULL && s!=first; s=s->to->next ) {
6487 	    if ( first==NULL ) first = s;
6488 	    if (( s->to->me.y<ii->x_height && s->from->me.y>ii->x_height+50) ||
6489 		    ( s->from->me.y<ii->x_height && s->to->me.y>ii->x_height+50)) {
6490 		if ( scnt>=2 ) {
6491 		    SplinePointListsFree(d_ss);
6492 return;
6493 		}
6494 		splines[scnt] = s;
6495 		sses[scnt++] = ss;
6496 	    }
6497 	}
6498     }
6499     if ( scnt!=2 || sses[0]!=sses[1] ) {
6500 	SplinePointListsFree(d_ss);
6501 return;
6502     }
6503     if ( splines[0]->from->me.x<splines[1]->from->me.x ) {
6504 	left = splines[0];
6505 	right = splines[1];
6506     } else {
6507 	left = splines[1];
6508 	right = splines[0];
6509     }
6510     stemwidth = right->from->me.x - left->from->me.x;
6511     drop = stemwidth*ii->tan_ia;
6512     if ( left->from->me.y<left->to->me.y ) {
6513 	min = left->from->me.y;
6514 	left_is_start = true;
6515     } else {
6516 	min = left->to->me.y;
6517 	left_is_start = false;
6518     }
6519     if ( ii->x_height+drop < min ) {
6520 	ltemp = left->from;
6521     } else {
6522 	CubicSolve(&left->splines[1],ii->x_height+drop,ts);
6523 	ltemp = SplineBisect(left,ts[0]);
6524     }
6525     CubicSolve(&right->splines[1],ii->x_height,ts);
6526     rtemp = SplineBisect(right,ts[0]);
6527     if ( left_is_start ) {
6528 	start = ltemp;
6529 	end   = rtemp;
6530     } else {
6531 	start = rtemp;
6532 	end   = ltemp;
6533     }
6534     SerifRemove(start,end,sses[0]);
6535     start->nonextcp = end->noprevcp = true;
6536     SplineMake(start,end,sc->layers[layer].order2);
6537  if ( sc->layers[layer].order2 )
6538   SSCPValidate(sses[0]);
6539 
6540     SCClearLayer(sc,layer);
6541     sc->layers[layer].splines = d_ss;
6542     sc->width = d->width;
6543     SplineCharAutoHint(sc,layer,NULL);
6544     FigureGoodStems(sc->vstem);
6545 }
6546 
_SCChangeXHeight(SplineChar * sc,int layer,struct xheightinfo * xi)6547 static void _SCChangeXHeight(SplineChar *sc,int layer,struct xheightinfo *xi) {
6548     int l;
6549     struct position_maps pmaps[7];
6550     struct fixed_maps fix;
6551     DBounds b;
6552     extern int no_windowing_ui;
6553     int nwi = no_windowing_ui;
6554 
6555     SplineCharLayerFindBounds(sc,layer,&b);
6556     no_windowing_ui = true;		/* Turn off undoes */
6557 
6558     l=0;
6559     fix.maps = pmaps;
6560     fix.maps[l].current = b.miny;
6561     fix.maps[l++].desired = b.miny;
6562     if ( b.miny<-xi->xheight_current/4 ) {
6563 	fix.maps[l].current = 0;
6564 	fix.maps[l++].desired = 0;
6565 	if ( xi->serif_height!=0 ) {
6566 	    fix.maps[l].current = xi->serif_height;
6567 	    fix.maps[l++].desired = xi->serif_height;
6568 	}
6569     }
6570     if ( xi->serif_height!=0 && b.maxy>=xi->xheight_current ) {
6571 	fix.maps[l].current = xi->xheight_current-xi->serif_height;
6572 	fix.maps[l++].desired = xi->xheight_desired-xi->serif_height;
6573     }
6574     if ( b.maxy>=xi->xheight_current ) {
6575 	fix.maps[l].current = xi->xheight_current;
6576 	fix.maps[l++].desired = xi->xheight_desired;
6577     }
6578     if ( b.maxy>=xi->xheight_desired ) {
6579 	fix.maps[l].current = b.maxy;
6580 	fix.maps[l++].desired = b.maxy;
6581     } else {
6582 	fix.maps[l].current = b.maxy;
6583 	fix.maps[l++].desired = b.maxy + xi->xheight_desired - xi->xheight_current;
6584     }
6585     fix.cnt = l;
6586     LowerCaseRemoveSpace(sc->layers[layer].splines,sc->anchor,sc->hstem,1,&fix);
6587     if ( xi->xheight_current!=0 ) {
6588 	double counter_len = SCFindCounterLen(sc->vstem,b.minx,b.maxx);
6589 	double remove_x = counter_len - xi->xheight_desired*counter_len/xi->xheight_current;
6590 	sc->width -= SmallCapsRemoveSpace(sc->layers[layer].splines,sc->anchor,sc->vstem,0,remove_x,b.minx,b.maxx);
6591     }
6592     SplineSetRefigure(sc->layers[layer].splines);
6593     no_windowing_ui = nwi;
6594 }
6595 
SCMakeItalic(SplineChar * sc,int layer,ItalicInfo * ii)6596 static void SCMakeItalic(SplineChar *sc,int layer,ItalicInfo *ii) {
6597     real skew[6], refpos[6];;
6598     RefChar *ref;
6599     extern int no_windowing_ui;
6600     int nwi = no_windowing_ui;
6601     int letter_case;
6602 
6603     SCPreserveLayer(sc,layer,true);
6604     no_windowing_ui = true;		/* Turn off undoes */
6605 
6606     if ( autohint_before_generate && (sc->changedsincelasthinted || sc->vstem==NULL ) &&
6607 	    !sc->manualhints )
6608 	SplineCharAutoHint(sc,layer,NULL);
6609     else if ( sc->dstem==NULL &&
6610 	    (sc->unicodeenc=='k' || sc->unicodeenc=='v' || sc->unicodeenc=='w' ||
6611 	     sc->unicodeenc=='x' || sc->unicodeenc=='y' || sc->unicodeenc==0x436 ||
6612 	     sc->unicodeenc==0x43a || sc->unicodeenc==0x443 || sc->unicodeenc==0x445 ||
6613 	     sc->unicodeenc==0x3ba || sc->unicodeenc==0x3bb || sc->unicodeenc==0x3bd ||
6614 	     sc->unicodeenc==0x3c7 ))
6615 	SplineCharAutoHint(sc,layer,NULL);	/* I will need diagonal stems */
6616     letter_case = FigureCase(sc);
6617     /* Small caps letters look like uppercase letters so should be transformed like them */
6618     if ( letter_case==cs_lc ) {
6619 	FigureGoodStems(sc->vstem);
6620 
6621 	if ( ii->a_from_d && sc->unicodeenc=='a' )
6622 	    Ital_a_From_d(sc,layer,ii);
6623 	if ( ii->a_from_d && sc->unicodeenc==0x430 )
6624 	    ItalReplaceWithReferenceTo(sc,layer,'a');
6625 	if ( ii->cyrl_i   && sc->unicodeenc==0x438 )
6626 	    ItalReplaceWithReferenceTo(sc,layer,'u');
6627 	else if ( ii->cyrl_pi  && sc->unicodeenc==0x43f )
6628 	    ItalReplaceWithReferenceTo(sc,layer,'n');
6629 	else if ( ii->cyrl_te  && sc->unicodeenc==0x442 )
6630 	    ItalReplaceWithReferenceTo(sc,layer,'m');
6631 	else if ( ii->cyrl_dje  && sc->unicodeenc==0x448 )
6632 	    ItalReplaceWithRotated(sc,layer,'m',ii);
6633 	else if ( ii->cyrl_dje  && sc->unicodeenc==0x452 )
6634 	    ItalReplaceWithSmallCaps(sc,layer,'T',ii);
6635 	if ( ii->cyrl_dzhe && sc->unicodeenc==0x45f )
6636 	    ItalReplaceWithReferenceTo(sc,layer,'u');
6637 
6638 	if (( ii->f_rotate_top || ii->f_long_tail) &&
6639 		( LikeAnF(sc) || (sc->unicodeenc==0x444 && ii->cyrl_phi)))
6640 	    FBottomTransform(sc,layer,ii);
6641 	else if ( LikePQ(sc) && ii->pq_deserif )
6642 	    DeSerifDescender(sc,layer,ii);
6643 	else if ( ii->transform_bottom_serifs )
6644 	    AddBottomItalicSerifs(sc,layer,ii);
6645 
6646 	if ( sc->unicodeenc==0x444 && ii->cyrl_phi )
6647 	    CyrilicPhiTop(sc,layer,ii);
6648 	else if ( ii->transform_top_xh_serifs || ii->transform_top_as_serifs )
6649 	    AddTopItalicSerifs(sc,layer,ii);
6650 
6651 	if ( ii->transform_diagon_serifs )
6652 	    AddDiagonalItalicSerifs(sc,layer,ii);
6653     }
6654 
6655     /* But small caps letters have the same stem sizes as lc so stems should be transformed like lc */
6656     ItalicCompress(sc,layer,letter_case==cs_lc? &ii->lc :
6657 			    letter_case==cs_uc? &ii->uc :
6658 			    letter_case==cs_smallcaps? &ii->lc :
6659 						&ii->neither);
6660     if (( letter_case==cs_lc || letter_case==cs_smallcaps ) &&
6661 	    sc->layers[layer].splines!=NULL &&
6662 	    ii->xheight_percent>0 && ii->xheight_percent!=1.0 ) {
6663 	struct xheightinfo xi;
6664 	memset(&xi,0,sizeof(xi));
6665 	xi.xheight_current = ii->x_height;
6666 	xi.xheight_desired = ii->xheight_percent*ii->x_height;
6667 	_SCChangeXHeight(sc,layer,&xi);
6668     }
6669 
6670     memset(skew,0,sizeof(skew));
6671     skew[0] = skew[3] = 1;
6672     skew[2] = -ii->tan_ia;
6673 
6674     SplinePointListTransform( sc->layers[layer].splines, skew, tpt_AllPoints );
6675     for ( ref=sc->layers[layer].refs; ref!=NULL; ref = ref->next ) {
6676 	memset(refpos,0,sizeof(refpos));
6677 	refpos[0] = refpos[3] = 1;
6678 	refpos[4] = -ii->tan_ia*ref->transform[5];
6679 	ref->transform[4] += refpos[4];
6680 	SplinePointListTransform(ref->layers[0].splines,refpos,tpt_AllPoints);
6681     }
6682 
6683     StemInfosFree(sc->hstem);  sc->hstem = NULL;
6684     StemInfosFree(sc->vstem);  sc->vstem = NULL;
6685     DStemInfosFree(sc->dstem); sc->dstem = NULL;
6686     no_windowing_ui = nwi;
6687     SCRound2Int(sc,layer, 1.0);		/* This calls SCCharChangedUpdate(sc,layer); */
6688 }
6689 
IsSelected(FontViewBase * fv,SplineChar * sc)6690 static int IsSelected(FontViewBase *fv,SplineChar *sc) {
6691     int enc = fv->map->backmap[sc->orig_pos];
6692     if ( enc==-1 )
6693 return( false );
6694 
6695 return( fv->selected[enc]);
6696 }
6697 
FVMakeAllItalic(FontViewBase * fv,SplineChar * sc,int layer,ItalicInfo * ii)6698 static int FVMakeAllItalic(FontViewBase *fv,SplineChar *sc,int layer,ItalicInfo *ii) {
6699     RefChar *ref;
6700 
6701     sc->ticked = true;
6702     for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
6703 	if ( !ref->sc->ticked && IsSelected(fv,ref->sc)) {
6704 	    if ( !FVMakeAllItalic(fv,ref->sc,layer,ii))
6705 return( false );
6706 	}
6707     }
6708     SCMakeItalic(sc,layer,ii);
6709 return( ff_progress_next());
6710 }
6711 
SerifExtent(SplineChar * sc,int layer,int is_bottom)6712 static double SerifExtent(SplineChar *sc,int layer,int is_bottom) {
6713     StemInfo *h;
6714     SplineSet *ss;
6715     SplinePoint *sp, *start, *end;
6716     double min=0, max=0;
6717     ItalicInfo tempii;
6718 
6719     if ( sc==NULL )
6720 return( 0 );
6721     memset(&tempii,0,sizeof(tempii));
6722     tempii.serif_extent = 1000;
6723 
6724     if ( autohint_before_generate && (sc->changedsincelasthinted || sc->vstem==NULL) &&
6725 	    !sc->manualhints )
6726 	SplineCharAutoHint(sc,layer,NULL);
6727     FigureGoodStems(sc->vstem);
6728     for ( h=sc->vstem; h!=NULL; h=h->next ) if ( h->tobeused ) {
6729 	if ( is_bottom )
6730 	    FindBottomSerifOnStem(sc,layer,h,0,&tempii,&start,&end,&ss);
6731 	else {
6732 	    DBounds b;
6733 	    SplineCharLayerFindBounds(sc,layer,&b);
6734 	    FindTopSerifOnStem(sc,layer,h,b.maxy,&tempii,&start,&end,&ss);
6735 	}
6736 	if ( start==NULL )
6737     continue;
6738 	for ( sp=start; sp!=end; sp=sp->next->to ) {
6739 	    if ( sp->me.x - (h->start+h->width) > max )
6740 		max = sp->me.x - (h->start+h->width);
6741 	    else if ( h->start - sp->me.x > min )
6742 		min = h->start - sp->me.x;
6743 	}
6744 	if ( min>max )
6745 return( min );
6746 	if ( max!=0 )
6747 return( max );
6748     }
6749 return( 0 );
6750 }
6751 
SCSerifHeight(SplineChar * sc,int layer)6752 static double SCSerifHeight(SplineChar *sc,int layer) {
6753     StemInfo *h;
6754     SplineSet *ss;
6755     SplinePoint *sp, *nsp, *start, *end;
6756     ItalicInfo tempii;
6757 
6758     if ( sc==NULL )
6759 return( 0 );
6760     memset(&tempii,0,sizeof(tempii));
6761     tempii.serif_extent = 1000;
6762 
6763     if ( autohint_before_generate && (sc->changedsincelasthinted || sc->vstem==NULL) &&
6764 	    !sc->manualhints )
6765 	SplineCharAutoHint(sc,layer,NULL);
6766     FigureGoodStems(sc->vstem);
6767     for ( h=sc->vstem; h!=NULL; h=h->next ) if ( h->tobeused ) {
6768 	FindBottomSerifOnStem(sc,layer,h,0,&tempii,&start,&end,&ss);
6769 	if ( start==NULL )
6770     continue;
6771 	for ( sp=start; sp!=end; sp=nsp ) {
6772 	    nsp = sp->next->to;
6773 	    if ( sp->me.y>5 && sp->me.y>=nsp->me.y-1 && sp->me.y<=nsp->me.y+1 )
6774 return( sp->me.y );
6775 	}
6776     }
6777 return( 0 );
6778 }
6779 
InitItalicConstants(SplineFont * sf,int layer,ItalicInfo * ii)6780 static void InitItalicConstants(SplineFont *sf, int layer, ItalicInfo *ii) {
6781     int i,cnt;
6782     double val;
6783 
6784     ii->tan_ia = tan(ii->italic_angle * FF_PI/180.0 );
6785 
6786     ii->x_height	  = SFXHeight  (sf,layer,false);
6787     ii->ascender_height   = SFAscender (sf,layer,false);
6788     ii->pq_depth	  = SFDescender(sf,layer,false);
6789 
6790     for ( i=cnt=0; lc_botserif_str[i]!=0; ++i ) {
6791 	val = SerifExtent(SFGetChar(sf,lc_botserif_str[i],NULL),layer,true);
6792 	if ( val>ii->serif_extent )
6793 	    ii->serif_extent = val;
6794     }
6795     for ( i=cnt=0; lc_topserif_str[i]!=0; ++i ) {
6796 	val = SerifExtent(SFGetChar(sf,lc_topserif_str[i],NULL),layer,false);
6797 	if ( val>ii->serif_extent )
6798 	    ii->serif_extent = val;
6799     }
6800 
6801     ii->emsize = sf->ascent+sf->descent;
6802     ii->order2 = sf->layers[layer].order2;
6803     ii->sf = sf;
6804     ii->layer = layer;
6805 }
6806 
StuffFree(SplinePoint * from,SplinePoint * to1,SplinePoint * to2)6807 static void StuffFree(SplinePoint *from, SplinePoint *to1, SplinePoint *to2) {
6808     SplinePoint *mid, *spnext;
6809 
6810     if ( from==NULL )
6811 return;
6812 
6813     for ( mid=from; mid!=to1 && mid!=to2; mid=spnext ) {
6814 	spnext = mid->next->to;
6815 	SplinePointFree(mid);
6816 	SplineFree(spnext->prev);
6817     }
6818     SplinePointFree(mid);
6819 }
6820 
ItalicInfoFreeContents(ItalicInfo * ii)6821 static void ItalicInfoFreeContents(ItalicInfo *ii) {
6822     StuffFree(ii->f_start,ii->f_end,NULL);
6823     StuffFree(ii->ff_start1,ii->ff_end1,ii->ff_end2);
6824     StuffFree(ii->ff_start2,ii->ff_end1,ii->ff_end2);
6825     memset(&ii->tan_ia,0,sizeof(ItalicInfo) - (((char *) &ii->tan_ia) - ((char *) ii)));
6826 }
6827 
MakeItalic(FontViewBase * fv,CharViewBase * cv,ItalicInfo * ii)6828 void MakeItalic(FontViewBase *fv,CharViewBase *cv, ItalicInfo *ii) {
6829     int cnt, enc, gid;
6830     SplineChar *sc;
6831     SplineFont *sf = fv!=NULL ? fv->sf : cv->sc->parent;
6832     int layer = fv!=NULL ? fv->active_layer : CVLayer(cv);
6833     extern int detect_diagonal_stems;
6834     int dds = detect_diagonal_stems;
6835 
6836     detect_diagonal_stems = true;
6837     InitItalicConstants(sf,layer,ii);
6838 
6839     if ( cv!=NULL )
6840 	SCMakeItalic(cv->sc,layer,ii);
6841     else {
6842 	cnt=0;
6843 
6844 	for ( enc=0; enc<fv->map->enccount; ++enc ) {
6845 	    if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
6846 		++cnt;
6847 		sc->ticked = false;
6848 	    }
6849 	}
6850 	if ( cnt!=0 ) {
6851 	    ff_progress_start_indicator(10,_("Italic"),
6852 		_("Italic Conversion"),NULL,cnt,1);
6853 
6854 	    for ( enc=0; enc<fv->map->enccount; ++enc ) {
6855 		if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] &&
6856 			(sc=sf->glyphs[gid])!=NULL && !sc->ticked ) {
6857 		    if ( !FVMakeAllItalic(fv,sc,layer,ii))
6858 	    break;
6859 		}
6860 	    }
6861 	    ff_progress_end_indicator();
6862 	}
6863     }
6864     detect_diagonal_stems = dds;
6865     ItalicInfoFreeContents(ii);
6866 }
6867 
6868 /* ************************************************************************** */
6869 /* ***************************** Change X-Height **************************** */
6870 /* ************************************************************************** */
6871 
InitXHeightInfo(SplineFont * sf,int layer,struct xheightinfo * xi)6872 void InitXHeightInfo(SplineFont *sf, int layer, struct xheightinfo *xi) {
6873     int i, j, cnt, besti;
6874     double val;
6875     const int MW=100;
6876     struct widths { double width, total; } widths[MW];
6877 
6878     memset(xi,0,sizeof(*xi));
6879     xi->xheight_current = SFXHeight(sf,layer,false);
6880     for ( i=cnt=0; lc_botserif_str[i]!=0; ++i ) {
6881 	val = SCSerifHeight(SFGetChar(sf,lc_botserif_str[i],NULL),layer);
6882 	if ( val!=0 ) {
6883 	    for ( j=0; j<cnt; ++j ) {
6884 		if ( widths[j].width==val ) {
6885 		    ++widths[j].total;
6886 	    break;
6887 		}
6888 	    }
6889 	    if ( j==cnt && j<MW ) {
6890 		widths[j].width = val;
6891 		widths[j].total = 1;
6892 	    }
6893 	}
6894     }
6895 
6896     besti = -1;
6897     for ( j=0 ; j<cnt; ++j ) {
6898 	if ( besti==-1 )
6899 	    besti = j;
6900 	else if ( widths[j].total >= widths[besti].total )
6901 	    besti = j;
6902     }
6903     if ( besti!=-1 )
6904 	xi->serif_height = widths[besti].total;
6905 }
6906 
SCChangeXHeight(SplineChar * sc,int layer,struct xheightinfo * xi)6907 static void SCChangeXHeight(SplineChar *sc,int layer,struct xheightinfo *xi) {
6908     const unichar_t *alts;
6909 
6910     if ( sc->layers[layer].refs!=NULL &&
6911 			((alts = SFGetAlternate(sc->parent,sc->unicodeenc,sc,true))!=NULL &&
6912 			 alts[1]!=0 ))
6913 	SCBuildComposit(sc->parent,sc,layer,NULL,true,false);
6914     else {
6915 	SCPreserveLayer(sc,layer,true);
6916 	_SCChangeXHeight(sc,layer,xi);
6917 	SCCharChangedUpdate(sc,layer);
6918     }
6919 }
6920 
FVChangeXHeight(FontViewBase * fv,SplineChar * sc,int layer,struct xheightinfo * xi)6921 static int FVChangeXHeight(FontViewBase *fv,SplineChar *sc,int layer,struct xheightinfo *xi) {
6922     RefChar *ref;
6923 
6924     sc->ticked = true;
6925     for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
6926 	if ( !ref->sc->ticked && IsSelected(fv,ref->sc)) {
6927 	    if ( !FVChangeXHeight(fv,ref->sc,layer,xi))
6928 return( false );
6929 	}
6930     }
6931     SCChangeXHeight(sc,layer,xi);
6932 return( ff_progress_next());
6933 }
6934 
ChangeXHeight(FontViewBase * fv,CharViewBase * cv,struct xheightinfo * xi)6935 void ChangeXHeight(FontViewBase *fv,CharViewBase *cv, struct xheightinfo *xi) {
6936     int cnt, enc, gid;
6937     SplineChar *sc;
6938     SplineFont *sf = fv!=NULL ? fv->sf : cv->sc->parent;
6939     int layer = fv!=NULL ? fv->active_layer : CVLayer(cv);
6940     extern int detect_diagonal_stems;
6941     int dds = detect_diagonal_stems;
6942 
6943     detect_diagonal_stems = true;
6944 
6945     if ( cv!=NULL )
6946 	SCChangeXHeight(cv->sc,layer,xi);
6947     else {
6948 	cnt=0;
6949 
6950 	for ( enc=0; enc<fv->map->enccount; ++enc ) {
6951 	    if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] && (sc=sf->glyphs[gid])!=NULL ) {
6952 		++cnt;
6953 		sc->ticked = false;
6954 	    }
6955 	}
6956 	if ( cnt!=0 ) {
6957 	    ff_progress_start_indicator(10,_("Change X-Height"),
6958 		_("Change X-Height"),NULL,cnt,1);
6959 
6960 	    for ( enc=0; enc<fv->map->enccount; ++enc ) {
6961 		if ( (gid=fv->map->map[enc])!=-1 && fv->selected[enc] &&
6962 			(sc=sf->glyphs[gid])!=NULL && !sc->ticked ) {
6963 		    if ( !FVChangeXHeight(fv,sc,layer,xi))
6964 	    break;
6965 		}
6966 	    }
6967 	    ff_progress_end_indicator();
6968 	}
6969     }
6970     detect_diagonal_stems = dds;
6971 }
6972