1 /* Copyright (C) 2000-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 
32 #include "chardata.h"
33 #include "cvundoes.h"
34 #include "dumppfa.h"
35 #include "edgelist.h"
36 #include "fontforge.h"
37 #include "psread.h"
38 #include "splinefill.h"
39 #include "splinefont.h"
40 #include "splinesave.h"
41 #include "splineutil.h"
42 #include "splineutil2.h"
43 #include "stemdb.h"
44 #include "tottfgpos.h"
45 #include "utype.h"
46 #include "views.h"
47 
48 #include <math.h>
49 #include <stdio.h>
50 
51 float OpenTypeLoadHintEqualityTolerance = 0.0;
52 
53 /* to create a type 1 font we must come up with the following entries for the
54   private dictionary:
55     BlueValues	-- an array of 2n entries where Blue[2i]<Blue[2i+1] max n=7, Blue[i]>0
56     OtherBlues	-- (optional) OtherBlue[i]<0
57     	(blue zones should be at least 3 units appart)
58     StdHW	-- (o) array with one entry, standard hstem height
59     StdVW	-- (o) ditto vstem width
60     StemSnapH	-- (o) up to 12 numbers containing various hstem heights (includes StdHW), small widths first
61     StemSnapV	-- (o) ditto, vstem
62 This file has routines to figure out at least some of these
63 
64 Also other routines to guess at good per-character hints
65 */
66 
AddBlue(real val,real array[5],int force)67 static void AddBlue(real val, real array[5], int force) {
68     val = rint(val);
69     if ( !force && (val<array[0]-array[1] || val >array[0]+array[1] ))
70 return;		/* Outside of one sd */
71     if ( array[3]==0 && array[4]==0 )
72 	array[3] = array[4] = val;
73     else if ( val>array[4] )
74 	array[4] = val;
75     else if ( val<array[3] )
76 	array[3] = val;
77 }
78 
MergeZones(real zone1[5],real zone2[5])79 static void MergeZones(real zone1[5], real zone2[5]) {
80     if ( zone1[2]!=0 && zone2[2]!=0 &&
81 	    ((zone1[4]+3>zone2[3] && zone1[3]<=zone2[3]) ||
82 	     (zone2[4]+3>zone1[3] && zone2[3]<=zone1[3]) )) {
83 	if (( zone2[0]<zone1[0]-zone1[1] || zone2[0] >zone1[0]+zone1[1] ) &&
84 		( zone1[0]<zone2[0]-zone2[1] || zone1[0] >zone2[0]+zone2[1] ))
85 	    /* the means of the zones are too far appart, don't merge em */;
86 	else {
87 	    if ( zone1[0]<zone2[0] ) zone2[0] = zone1[0];
88 	    if ( zone1[1]>zone2[1] ) zone2[1] = zone1[1];
89 	}
90 	zone1[2] = 0;
91     }
92 }
93 
94 /* I can deal with latin, greek and cyrillic because the they all come from */
95 /*  the same set of letter shapes and have all evolved together and have */
96 /*  various common features (ascenders, descenders, lower case, etc.). Other */
97 /*  scripts don't fit */
FindBlues(SplineFont * sf,int layer,real blues[14],real otherblues[10])98 void FindBlues( SplineFont *sf, int layer, real blues[14], real otherblues[10]) {
99     real caph[5], xh[5], ascenth[5], digith[5], descenth[5], base[5];
100     real otherdigits[5];
101     int i, j, k;
102     DBounds b;
103 
104     /* Go through once to get some idea of the average value so we can weed */
105     /*  out undesirables */
106     caph[0] = caph[1] = caph[2] = xh[0] = xh[1] = xh[2] = 0;
107     ascenth[0] = ascenth[1] = ascenth[2] = digith[0] = digith[1] = digith[2] = 0;
108     descenth[0] = descenth[1] = descenth[2] = base[0] = base[1] = base[2] = 0;
109     otherdigits[0] = otherdigits[1] = otherdigits[2] = 0;
110     for ( i=0; i<sf->glyphcnt; ++i ) {
111 	if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->layers[layer].splines!=NULL ) {
112 	    int enc = sf->glyphs[i]->unicodeenc;
113 	    const unichar_t *upt;
114 	    if ( enc<0x10000 && isalnum(enc) &&
115 		    ((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf ||
116 		      enc==0x131 ||
117 		     (enc>=0x391 && enc<=0x3f3 ) ||
118 		     (enc>=0x400 && enc<=0x4e9 ) )) {
119 		/* no accented characters (or ligatures) */
120 		if ( unicode_alternates[enc>>8]!=NULL &&
121 			(upt =unicode_alternates[enc>>8][enc&0xff])!=NULL &&
122 			upt[1]!='\0' )
123     continue;
124 		SplineCharFindBounds(sf->glyphs[i],&b);
125 		if ( b.miny==0 && b.maxy==0 )
126     continue;
127 		if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' ||
128 			enc==0xfe ||
129 			enc==0x3c1 /* rho */ ||
130 			enc==0x3c6 /* phi */ ||
131 			enc==0x3c7 /* chi */ ||
132 			enc==0x3c8 /* psi */ ||
133 			enc==0x440 /* cyr er */ ||
134 			enc==0x443 /* cyr u */ ||
135 			enc==0x444 /* cyr ef */) {
136 		    descenth[0] += b.miny;
137 		    descenth[1] += b.miny*b.miny;
138 		    ++descenth[2];
139 		} else if ( enc=='x' || enc=='r' || enc=='o' || enc=='e' ||
140 		               enc=='s' || enc=='c' || enc=='h' || enc=='k' ||
141 			       enc=='l' || enc=='m' || enc=='n' ||
142 			       enc==0x3b5 /* epsilon */ ||
143 			       enc==0x3b9 /* iota */ ||
144 			       enc==0x3ba /* kappa */ ||
145 			       enc==0x3bf /* omicron */ ||
146 			       enc==0x3c3 /* sigma */ ||
147 			       enc==0x3c5 /* upsilon */ ||
148 			       enc==0x430 /* cyr a */ ||
149 			       enc==0x432 /* cyr ve */ ||
150 			       enc==0x433 /* cyr ge */ ||
151 			       enc==0x435 /* cyr e */ ||
152 			       enc==0x436 /* cyr zhe */ ||
153 			       enc==0x438 /* cyr i */ ||
154 			       enc==0x43a /* cyr ka */ ||
155 			       enc==0x43d /* cyr en */ ||
156 			       enc==0x43e /* cyr o */ ||
157 			       enc==0x441 /* cyr es */ ||
158 			       enc==0x445 /* cyr ha */ ||
159 			       enc==0x447 /* cyr che */ ||
160 			       enc==0x448 /* cyr sha */ ||
161 			       enc==0x44f /* cyr ya */ ){
162 		    base[0] += b.miny;
163 		    base[1] += b.miny*b.miny;
164 		    ++base[2];
165 		}
166 		/* careful of lowercase digits, 6 and 8 should be ascenders */
167 		if ( enc=='6' || enc=='8' ) {
168 		    digith[0] += b.maxy;
169 		    digith[1] += b.maxy*b.maxy;
170 		    ++digith[2];
171 		} else if ( enc<0x10000 && isdigit(enc) ) {
172 		    otherdigits[0] += b.maxy;
173 		    otherdigits[1] += b.maxy*b.maxy;
174 		    ++otherdigits[2];
175 		} else if ( enc<0x10000 && isupper(enc) && enc!=0x462 && enc!=0x490 ) {
176 		    caph[0] += b.maxy;
177 		    caph[1] += b.maxy*b.maxy;
178 		    ++caph[2];
179 		} else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' ||
180 			enc == 'l' || enc==0xf0 || enc==0xfe || enc == 0xdf ||
181 			enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb ||
182 			enc == 0x3be ||
183 			enc == 0x431 /* cyr be */ /* || enc == 0x444 - ef may have varible height */) {
184 		    ascenth[0] += b.maxy;
185 		    ascenth[1] += b.maxy*b.maxy;
186 		    ++ascenth[2];
187 		} else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' ||
188 		            enc=='v' || enc=='w' || enc=='x' || enc=='y' || enc=='z' ||
189 			    enc==0x3b5 /* epsilon */ ||
190 			    enc==0x3b9 /* iota */ ||
191 			    enc==0x3ba /* kappa */ ||
192 			    enc==0x3bc /* mu */ ||
193 			    enc==0x3bd /* nu */ ||
194 			    enc==0x3bf /* omicron */ ||
195 			    enc==0x3c0 /* pi */ ||
196 			    enc==0x3c1 /* rho */ ||
197 			    enc==0x3c5 /* upsilon */ ||
198 			    enc==0x433 /* cyr ge */ ||
199 			    enc==0x435 /* cyr e */ ||
200 			    enc==0x436 /* cyr zhe */ ||
201 			    enc==0x438 /* cyr i */ ||
202 			    enc==0x43b /* cyr el */ ||
203 			    enc==0x43d /* cyr en */ ||
204 			    enc==0x43e /* cyr o */ ||
205 			    enc==0x43f /* cyr pe */ ||
206 			    enc==0x440 /* cyr er */ ||
207 			    enc==0x441 /* cyr es */ ||
208 			    enc==0x442 /* cyr te */ ||
209 			    enc==0x443 /* cyr u */ ||
210 			    enc==0x445 /* cyr ha */ ||
211 			    enc==0x446 /* cyr tse */ ||
212 			    enc==0x447 /* cyr che */ ||
213 			    enc==0x448 /* cyr sha */ ||
214 			    enc==0x449 /* cyr shcha */ ||
215 			    enc==0x44a /* cyr hard sign */ ||
216 			    enc==0x44b /* cyr yery */ ||
217 			    enc==0x44c /* cyr soft sign */ ||
218 			    enc==0x44d /* cyr reversed e */ ||
219 			    enc==0x44f /* cyr ya */ ) {
220 		    xh[0] += b.maxy;
221 		    xh[1] += b.maxy*b.maxy;
222 		    ++xh[2];
223 		}
224 	    }
225 	}
226 	if ( !ff_progress_next())
227     break;
228     }
229     if ( otherdigits[2]>0 && digith[2]>0 ) {
230 	if ( otherdigits[0]/otherdigits[2] >= .95*digith[0]/digith[2] ) {
231 	    /* all digits are about the same height, not lowercase */
232 	    digith[0] += otherdigits[0];
233 	    digith[1] += otherdigits[1];
234 	    digith[2] += otherdigits[2];
235 	}
236     }
237 
238     if ( xh[2]>1 ) {
239 	xh[1] = sqrt((xh[1]-xh[0]*xh[0]/xh[2])/(xh[2]-1));
240 	xh[0] /= xh[2];
241     }
242     if ( ascenth[2]>1 ) {
243 	ascenth[1] = sqrt((ascenth[1]-ascenth[0]*ascenth[0]/ascenth[2])/(ascenth[2]-1));
244 	ascenth[0] /= ascenth[2];
245     }
246     if ( caph[2]>1 ) {
247 	caph[1] = sqrt((caph[1]-caph[0]*caph[0]/caph[2])/(caph[2]-1));
248 	caph[0] /= caph[2];
249     }
250     if ( digith[2]>1 ) {
251 	digith[1] = sqrt((digith[1]-digith[0]*digith[0]/digith[2])/(digith[2]-1));
252 	digith[0] /= digith[2];
253     }
254     if ( base[2]>1 ) {
255 	base[1] = sqrt((base[1]-base[0]*base[0]/base[2])/(base[2]-1));
256 	base[0] /= base[2];
257     }
258     if ( descenth[2]>1 ) {
259 	descenth[1] = sqrt((descenth[1]-descenth[0]*descenth[0]/descenth[2])/(descenth[2]-1));
260 	descenth[0] /= descenth[2];
261     }
262 
263     /* we'll accept values between +/- 1sd of the mean */
264     /* array[0] == mean, array[1] == sd, array[2] == cnt, array[3]=min, array[4]==max */
265     if ( base[0]+base[1]<0 ) base[1] = -base[0];	/* Make sure 0 is within the base bluezone */
266     caph[3] = caph[4] = 0;
267     xh[3] = xh[4] = 0;
268     ascenth[3] = ascenth[4] = 0;
269     digith[3] = digith[4] = 0;
270     descenth[3] = descenth[4] = 0;
271     base[3] = base[4] = 0;
272     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
273 	int enc = sf->glyphs[i]->unicodeenc;
274 	const unichar_t *upt;
275 	if ( enc<0x10000 && isalnum(enc) &&
276 		((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf ||
277 		 (enc>=0x391 && enc<=0x3f3 ) ||
278 		 (enc>=0x400 && enc<=0x4e9 ) )) {
279 	    /* no accented characters (or ligatures) */
280 	    if ( unicode_alternates[enc>>8]!=NULL &&
281 		    (upt =unicode_alternates[enc>>8][enc&0xff])!=NULL &&
282 		    upt[1]!='\0' )
283     continue;
284 	    SplineCharFindBounds(sf->glyphs[i],&b);
285 	    if ( b.miny==0 && b.maxy==0 )
286     continue;
287 	    if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' ||
288 		    enc==0xfe || enc == 0x3c6 || enc==0x3c8 ||
289 		    enc==0x440 || enc==0x443 || enc==0x444) {
290 		AddBlue(b.miny,descenth,false);
291 	    } else {
292 		/* O and o get forced into the baseline blue value even if they*/
293 		/*  are beyond 1 sd */
294 		AddBlue(b.miny,base,enc=='O' || enc=='o');
295 	    }
296 	    if ( enc<0x10000 && isdigit(enc)) {
297 		AddBlue(b.maxy,digith,false);
298 	    } else if ( enc<0x10000 && isupper(enc)) {
299 		AddBlue(b.maxy,caph,enc=='O');
300 	    } else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' ||
301 		    enc == 'l' || enc=='t' || enc==0xf0 || enc==0xfe || enc == 0xdf ||
302 		    enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb ||
303 		    enc == 0x3be ||
304 		    enc == 0x431 ) {
305 		AddBlue(b.maxy,ascenth,false);
306 	    } else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' ||
307 			enc=='v' || enc=='w' || enc=='x' || enc=='y' || enc=='z' ||
308 			enc==0x3b5 /* epsilon */ ||
309 			enc==0x3b9 /* iota */ ||
310 			enc==0x3ba /* kappa */ ||
311 			enc==0x3bc /* mu */ ||
312 			enc==0x3bd /* nu */ ||
313 			enc==0x3bf /* omicron */ ||
314 			enc==0x3c0 /* pi */ ||
315 			enc==0x3c1 /* rho */ ||
316 			enc==0x3c5 /* upsilon */ ||
317 			enc==0x433 /* cyr ge */ ||
318 			enc==0x435 /* cyr e */ ||
319 			enc==0x436 /* cyr zhe */ ||
320 			enc==0x438 /* cyr i */ ||
321 			enc==0x43b /* cyr el */ ||
322 			enc==0x43d /* cyr en */ ||
323 			enc==0x43e /* cyr o */ ||
324 			enc==0x43f /* cyr pe */ ||
325 			enc==0x440 /* cyr er */ ||
326 			enc==0x441 /* cyr es */ ||
327 			enc==0x442 /* cyr te */ ||
328 			enc==0x443 /* cyr u */ ||
329 			enc==0x445 /* cyr ha */ ||
330 			enc==0x446 /* cyr tse */ ||
331 			enc==0x447 /* cyr che */ ||
332 			enc==0x448 /* cyr sha */ ||
333 			enc==0x449 /* cyr shcha */ ||
334 			enc==0x44a /* cyr hard sign */ ||
335 			enc==0x44b /* cyr yery */ ||
336 			enc==0x44c /* cyr soft sign */ ||
337 			enc==0x44d /* cyr reversed e */ ||
338 			enc==0x44f /* cyr ya */ ) {
339 		AddBlue(b.maxy,xh,enc=='o' || enc=='x');
340 	    }
341 	}
342     }
343 
344     /* the descent blue zone merges into the base zone */
345     MergeZones(descenth,base);
346     MergeZones(xh,base);
347     MergeZones(ascenth,caph);
348     MergeZones(digith,caph);
349     MergeZones(xh,caph);
350     MergeZones(ascenth,digith);
351     MergeZones(xh,digith);
352 
353     if ( otherblues!=NULL )
354 	for ( i=0; i<10; ++i )
355 	    otherblues[i] = 0;
356     for ( i=0; i<14; ++i )
357 	blues[i] = 0;
358 
359     if ( otherblues!=NULL && descenth[2]!=0 ) {
360 	otherblues[0] = descenth[3];
361 	otherblues[1] = descenth[4];
362     }
363     i = 0;
364     if ( base[2]==0 && (xh[2]!=0 || ascenth[2]!=0 || caph[2]!=0 || digith[2]!=0 )) {
365 	/* base line blue value must be present if any other value is */
366 	/*  make one up if we don't have one */
367 	blues[0] = -20;
368 	blues[1] = 0;
369 	i = 2;
370     } else if ( base[2]!=0 ) {
371 	blues[0] = base[3];
372 	blues[1] = base[4];
373 	i = 2;
374     }
375     if ( xh[2]!=0 ) {
376 	blues[i++] = xh[3];
377 	blues[i++] = xh[4];
378     }
379     if ( caph[2]!=0 ) {
380 	blues[i++] = caph[3];
381 	blues[i++] = caph[4];
382     }
383     if ( digith[2]!=0 ) {
384 	blues[i++] = digith[3];
385 	blues[i++] = digith[4];
386     }
387     if ( ascenth[2]!=0 ) {
388 	blues[i++] = ascenth[3];
389 	blues[i++] = ascenth[4];
390     }
391 
392     for ( j=0; j<i; j+=2 ) for ( k=j+2; k<i; k+=2 ) {
393 	if ( blues[j]>blues[k] ) {
394 	    real temp = blues[j];
395 	    blues[j]=blues[k];
396 	    blues[k] = temp;
397 	    temp = blues[j+1];
398 	    blues[j+1] = blues[k+1];
399 	    blues[k+1] = temp;
400 	}
401     }
402 }
403 
PVAddBlues(BlueData * bd,unsigned bcnt,char * pt)404 static int PVAddBlues(BlueData *bd,unsigned bcnt,char *pt) {
405     char *end;
406     real val1, val2;
407     unsigned i,j;
408 
409     if ( pt==NULL )
410 return( bcnt );
411 
412     while ( isspace(*pt) || *pt=='[' ) ++pt;
413     while ( *pt!=']' && *pt!='\0' ) {
414 	val1 = strtod(pt,&end);
415 	if ( *end=='\0' || end==pt )
416     break;
417 	for ( pt=end; isspace(*pt) ; ++pt );
418 	val2 = strtod(pt,&end);
419 	if ( end==pt )
420     break;
421 	if ( bcnt==0 || val1>bd->blues[bcnt-1][0] )
422 	    i = bcnt;
423 	else {
424 	    for ( i=0; i<bcnt && val1>bd->blues[i][0]; ++i );
425 	    for ( j=bcnt; j>i; --j ) {
426 		bd->blues[j][0] = bd->blues[j-1][0];
427 		bd->blues[j][1] = bd->blues[j-1][1];
428 	    }
429 	}
430 	bd->blues[i][0] = val1;
431 	bd->blues[i][1] = val2;
432 	++bcnt;
433 	if ( bcnt>=sizeof(bd->blues)/sizeof(bd->blues[0]))
434     break;
435 	for ( pt=end; isspace(*pt) ; ++pt );
436     }
437 return( bcnt );
438 }
439 
440 /* Quick and dirty (and sometimes wrong) approach to figure out the common */
441 /* alignment zones in latin (greek, cyrillic) alphabets */
QuickBlues(SplineFont * _sf,int layer,BlueData * bd)442 void QuickBlues(SplineFont *_sf, int layer, BlueData *bd) {
443     real xheight = -1e10, caph = -1e10, ascent = -1e10, descent = 1e10, max, min;
444     real xheighttop = -1e10, caphtop = -1e10;
445     real numh = -1e10, numhtop = -1e10;
446     real base = -1e10, basebelow = 1e10;
447     SplineFont *sf;
448     SplinePoint *sp;
449     SplineSet *spl;
450     int i,j, bcnt;
451     SplineChar *t;
452     char *pt;
453 
454     /* Get the alignment zones we care most about */
455 
456     /* be careful of cid fonts */
457     if ( _sf->cidmaster!=NULL )
458 	_sf = _sf->cidmaster;
459 
460     j=0;
461     do {
462 	sf = ( _sf->subfontcnt==0 )? _sf : _sf->subfonts[j];
463 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
464 	    int enc = sf->glyphs[i]->unicodeenc;
465 	    if ( enc=='I' || enc=='O' || enc=='x' || enc=='o' || enc=='p' || enc=='l' ||
466 /* Jean-Christophe Dubacq points out that x-height should be calculated from */
467 /*  various characters and not just x and o. Italic "x"s often have strange */
468 /*  shapes */
469 		    enc=='A' || enc==0x391 || enc==0x410 ||
470 		    enc=='V' ||
471 		    enc=='u' || enc=='v' || enc=='w' || enc=='y' || enc=='z' ||
472 		    enc=='7' || enc=='8' ||	/* numbers with ascenders */
473 		    enc==0x399 || enc==0x39f || enc==0x3ba || enc==0x3bf || enc==0x3c1 || enc==0x3be || enc==0x3c7 ||
474 		    enc==0x41f || enc==0x41e || enc==0x43e || enc==0x43f || enc==0x440 || enc==0x452 || enc==0x445 ) {
475 		t = sf->glyphs[i];
476 		while ( t->layers[layer].splines==NULL && t->layers[layer].refs!=NULL )
477 		    t = t->layers[layer].refs->sc;
478 		max = -1e10; min = 1e10;
479 		if ( t->layers[layer].splines!=NULL ) {
480 		    for ( spl=t->layers[layer].splines; spl!=NULL; spl=spl->next ) {
481 			for ( sp=spl->first; ; ) {
482 			    if ( sp->me.y > max ) max = sp->me.y;
483 			    if ( sp->me.y < min ) min = sp->me.y;
484 			    if ( sp->next==NULL )
485 			break;
486 			    sp = sp->next->to;
487 			    if ( sp==spl->first )
488 			break;
489 			}
490 		    }
491 		    if ( enc>0x400 ) {
492 			/* Only use ascent and descent here if we don't have anything better */
493 			if ( enc==0x41f ) { caph = max; base = min; }
494 			else if ( enc==0x41e ) { if ( max>caphtop ) caphtop = max; basebelow = min; }
495 			else if ( enc==0x410 ) { if ( max>caphtop ) caphtop = max; }
496 			else if ( enc==0x43f && xheight<0 ) xheight = max;
497 			else if ( enc==0x445 && xheight<0 ) xheight = max;
498 			else if ( enc==0x43e ) xheighttop = max;
499 			else if ( enc==0x452 && ascent<0 ) ascent = max;
500 			else if ( enc==0x440 && descent>0 ) descent = min;
501 		    } else if ( enc>0x300 ) {
502 			if ( enc==0x399 ) { caph = max; base = min; }
503 			else if ( enc==0x391 ) { if ( max>caphtop ) caphtop = max; }
504 			else if ( enc==0x39f ) { if ( max>caphtop ) caphtop = max; basebelow = min; }
505 			else if ( enc==0x3ba && xheight<0 ) xheight = max;
506 			else if ( enc==0x3c7 && xheight<0 ) xheight = max;
507 			else if ( enc==0x3bf ) xheighttop = max;
508 			else if ( enc==0x3be && ascent<0 ) ascent = max;
509 			else if ( enc==0x3c1 && descent>0 ) descent = min;
510 		    } else {
511 			if ( enc=='I' ) { caph = max; base = min; }
512 			else if ( enc=='O' ) { if ( max>caphtop ) caphtop = max; if ( basebelow<min ) basebelow = min; }
513 			else if ( enc=='V' ) { if ( basebelow<min ) basebelow = min; }
514 			else if ( enc=='A' ) { if ( max>caphtop ) caphtop = max; }
515 			else if ( enc=='7' ) numh = max;
516 			else if ( enc=='0' ) numhtop = max;
517 			else if ( enc=='x' || enc=='o' || enc=='u' || enc=='v' ||
518 				enc =='w' || enc=='y' || enc=='z' ) {
519 			    if ( xheighttop==-1e10 ) xheighttop = max;
520 			    if ( xheight==-1e10 ) xheight = max;
521 			    if ( max > xheighttop ) xheighttop = max;
522 			    else if ( max<xheight && max>20 ) xheight = max;
523 			    if ( enc=='y' && descent==1e10 ) descent = min;
524 			} else if ( enc=='l' ) ascent = max;
525 			else descent = min;
526 		    }
527 		}
528 	    }
529 	}
530 	++j;
531     } while ( j<_sf->subfontcnt );
532 
533     if ( basebelow==1e10 ) basebelow=-1e10;
534 
535     if ( caphtop<caph ) caphtop = caph; else if ( caph==-1e10 ) caph=caphtop;
536     if ( basebelow>base ) basebelow = base; else if ( base==-1e10 ) base=basebelow;
537     if ( base==-1e10 ) { base=basebelow = 0; }
538     if ( xheighttop<xheight ) xheighttop = xheight; else if ( xheight==-1e10 ) xheight=xheighttop;
539     bd->xheight = xheight; bd->xheighttop = xheighttop;
540     bd->caph = caph; bd->caphtop = caphtop;
541     bd->numh = numh; bd->numhtop = numhtop;
542     bd->ascent = ascent; bd->descent = descent;
543     bd->base = base; bd->basebelow = basebelow;
544 
545     bcnt = 0;
546     if ( (pt=PSDictHasEntry(sf->private,"BlueValues"))!=NULL )
547 	bcnt = PVAddBlues(bd,bcnt,pt);
548     if ( (pt=PSDictHasEntry(sf->private,"OtherBlues"))!=NULL )
549 	bcnt = PVAddBlues(bd,bcnt,pt);
550     if ( bcnt==0 ) {
551 	if ( basebelow==-1e10 ) basebelow = base;
552 	if ( base==-1e10 ) base = basebelow;
553 	if ( xheight==-1e10 ) xheight = xheighttop;
554 	if ( xheighttop==-1e10 ) xheighttop = xheight;
555 	if ( caph==-1e10 ) caph = caphtop;
556 	if ( caphtop==-1e10 ) caphtop = caph;
557 	if ( numh==-1e10 ) numh = numhtop;
558 	if ( numhtop==-1e10 ) numhtop = numh;
559 	if ( numh!=-1e10 && (numhtop>caph-2 && numh<caphtop+2)) {
560 	    if ( numh<caph ) caph=numh;
561 	    if ( numhtop>caphtop ) caphtop = numhtop;
562 	    numh = numhtop = -1e10;
563 	}
564 	if ( ascent!=-1e10 && (ascent>caph-2 && ascent<caphtop+2)) {
565 	    if ( ascent<caph ) caph=ascent;
566 	    if ( ascent>caphtop ) caphtop = ascent;
567 	    ascent = -1e10;
568 	}
569 	if ( ascent!=-1e10 && (ascent>numh-2 && ascent<numhtop+2)) {
570 	    if ( ascent<numh ) numh=ascent;
571 	    if ( ascent>numhtop ) numhtop = ascent;
572 	    ascent = -1e10;
573 	    if ( numhtop>caph-2 && numh<caphtop+2 ) {
574 		if ( numh<caph ) caph=numh;
575 		if ( numhtop>caphtop ) caphtop = numhtop;
576 		numh = numhtop = -1e10;
577 	    }
578 	}
579 
580 	if ( descent!=1e10 ) {
581 	    bd->blues[0][0] = bd->blues[0][1] = descent;
582 	    ++bcnt;
583 	}
584 	if ( basebelow!=-1e10 ) {
585 	    bd->blues[bcnt][0] = basebelow;
586 	    bd->blues[bcnt][1] = base;
587 	    ++bcnt;
588 	}
589 	if ( xheight!=-1e10 ) {
590 	    bd->blues[bcnt][0] = xheight;
591 	    bd->blues[bcnt][1] = xheighttop;
592 	    ++bcnt;
593 	}
594 	if ( numh!=-1e10 ) {
595 	    bd->blues[bcnt][0] = numh;
596 	    bd->blues[bcnt][1] = numhtop;
597 	    ++bcnt;
598 	}
599 	if ( caph!=-1e10 ) {
600 	    bd->blues[bcnt][0] = caph;
601 	    bd->blues[bcnt][1] = caphtop;
602 	    ++bcnt;
603 	}
604 	if ( ascent!=-1e10 ) {
605 	    bd->blues[bcnt][0] = bd->blues[bcnt][1] = ascent;
606 	    ++bcnt;
607 	}
608     }
609     bd->bluecnt = bcnt;
610 }
611 
ElFreeEI(EIList * el)612 void ElFreeEI(EIList *el) {
613     EI *e, *next;
614 
615     for ( e = el->edges; e!=NULL; e = next ) {
616 	next = e->next;
617 	free(e);
618     }
619 }
620 
EIAddEdge(Spline * spline,real tmin,real tmax,EIList * el)621 static int EIAddEdge(Spline *spline, real tmin, real tmax, EIList *el) {
622     EI *new = calloc(1,sizeof(EI));
623     real min, max, temp;
624     Spline1D *s;
625     real dxdtmin, dxdtmax, dydtmin, dydtmax;
626 
627     new->spline = spline;
628     new->tmin = tmin;
629     new->tmax = tmax;
630 
631     s = &spline->splines[1];
632     if (( dydtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dydtmin = -dydtmin;
633     if (( dydtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dydtmax = -dydtmax;
634     s = &spline->splines[0];
635     if (( dxdtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dxdtmin = -dxdtmin;
636     if (( dxdtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dxdtmax = -dxdtmax;
637 
638     /*s = &spline->splines[0];*/
639     min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d;
640     max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d;
641     if ( tmax==1 ) max = spline->to->me.x;	/* beware rounding errors */
642     if ( !el->leavetiny && floor(min)==floor(max) ) {	/* If it doesn't cross a pixel boundary then it might as well be vertical */
643 	if ( tmin==0 ) max = min;
644 	else if ( tmax==1 ) min = max;
645 	else max = min;
646     }
647     if ( min==max )
648 	new->vert = true;
649     else if ( min<max )
650 	new->hup = true;
651     else {
652 	temp = min; min = max; max=temp;
653     }
654     if ( !el->leavetiny && min+1>max ) new->almostvert = true;
655     if ( 40*dxdtmin<dydtmin ) new->vertattmin = true;
656     if ( 40*dxdtmax<dydtmax ) new->vertattmax = true;
657     /*if ( new->vertattmin && new->vertattmax && s->a==0 && s->b==0 ) new->almostvert = true;*/
658     new->coordmin[0] = min; new->coordmax[0] = max;
659     if ( el->coordmin[0]>min )
660 	el->coordmin[0] = min;
661     if ( el->coordmax[0]<max )
662 	el->coordmax[0] = max;
663 
664     s = &spline->splines[1];
665     min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d;
666     max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d;
667     if ( tmax==1 ) max = spline->to->me.y;
668     if ( !el->leavetiny && floor(min)==floor(max) ) {	/* If it doesn't cross a pixel boundary then it might as well be horizontal */
669 	if ( tmin==0 ) max = min;
670 	else if ( tmax==1 ) min = max;
671 	else max = min;
672     }
673     if ( min==max )
674 	new->hor = true;
675     else if ( min<max )
676 	new->vup = true;
677     else {
678 	temp = min; min = max; max=temp;
679     }
680     if ( !el->leavetiny && min+1>max ) new->almosthor = true;
681     if ( 40*dydtmin<dxdtmin ) new->horattmin = true;
682     if ( 40*dydtmax<dxdtmax ) new->horattmax = true;
683     /*if ( new->horattmin && new->horattmax && s->a==0 && s->b==0 ) new->almosthor = true;*/
684     new->coordmin[1] = min; new->coordmax[1] = max;
685     if ( el->coordmin[1]>min )
686 	el->coordmin[1] = min;
687     if ( el->coordmax[1]<max )
688 	el->coordmax[1] = max;
689 
690     if ( new->hor && new->vert ) {
691 	/* This spline is too small for us to notice */
692 	free(new);
693 return( false );
694     } else {
695 	new->next = el->edges;
696 	el->edges = new;
697 
698 	if ( el->splinelast!=NULL )
699 	    el->splinelast->splinenext = new;
700 	el->splinelast = new;
701 	if ( el->splinefirst==NULL )
702 	    el->splinefirst = new;
703 
704 return( true );
705     }
706 }
707 
EIAddSpline(Spline * spline,EIList * el)708 static void EIAddSpline(Spline *spline, EIList *el) {
709     extended ts[6], temp;
710     int i, j, base, last;
711 
712     ts[0] = 0; ts[5] = 1.0;
713     SplineFindExtrema(&spline->splines[0],&ts[1],&ts[2]);
714     SplineFindExtrema(&spline->splines[1],&ts[3],&ts[4]);
715     /* avoid teeny tiny segments, they just confuse us */
716     SplineRemoveExtremaTooClose(&spline->splines[0],&ts[1],&ts[2]);
717     SplineRemoveExtremaTooClose(&spline->splines[1],&ts[3],&ts[4]);
718     for ( i=0; i<4; ++i ) for ( j=i+1; j<5; ++j ) {
719 	if ( ts[i]>ts[j] ) {
720 	    temp = ts[i];
721 	    ts[i] = ts[j];
722 	    ts[j] = temp;
723 	}
724     }
725     for ( base=0; ts[base]==-1; ++base);
726     for ( i=5; i>base ; --i ) {
727 	if ( ts[i]==ts[i-1] ) {
728 	    for ( j=i-1; j>base; --j )
729 		ts[j] = ts[j-1];
730 	    ts[j] = -1;
731 	    ++base;
732 	}
733     }
734     last = base;
735     for ( i=base; i<5 ; ++i )
736 	if ( EIAddEdge(spline,ts[last],ts[i+1],el) )
737 	    last = i+1;
738 }
739 
ELFindEdges(SplineChar * sc,EIList * el)740 void ELFindEdges(SplineChar *sc, EIList *el) {
741     Spline *spline, *first;
742     SplineSet *spl;
743 
744     el->sc = sc;
745     if ( sc->layers[el->layer].splines==NULL )
746 return;
747 
748     el->coordmin[0] = el->coordmax[0] = sc->layers[el->layer].splines->first->me.x;
749     el->coordmin[1] = el->coordmax[1] = sc->layers[el->layer].splines->first->me.y;
750 
751     for ( spl = sc->layers[el->layer].splines; spl!=NULL; spl = spl->next ) if ( spl->first->prev!=NULL && spl->first->prev->from!=spl->first ) {
752 	first = NULL;
753 	for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
754 	    EIAddSpline(spline,el);
755 	    if ( first==NULL ) first = spline;
756 	}
757 	if ( el->splinefirst!=NULL && spl->first->prev!=NULL )
758 	    el->splinelast->splinenext = el->splinefirst;
759 	el->splinelast = NULL; el->splinefirst = NULL;
760     }
761 }
762 
IsBiggerSlope(EI * test,EI * base,int major)763 static int IsBiggerSlope(EI *test, EI *base, int major) {
764     int other = !major;
765     real tdo, tdm, bdo, bdm, t;
766 
767     if (( major && test->vup ) || (!major && test->hup))
768 	t = test->tmin;
769     else
770 	t = test->tmax;
771     tdm = (3*test->spline->splines[major].a*t + 2*test->spline->splines[major].b)*t + test->spline->splines[major].c;
772     tdo = (3*test->spline->splines[other].a*t + 2*test->spline->splines[other].b)*t + test->spline->splines[other].c;
773 
774     if (( major && base->vup ) || (!major && base->hup))
775 	t = base->tmin;
776     else
777 	t = base->tmax;
778     bdm = (3*base->spline->splines[major].a*t + 2*base->spline->splines[major].b)*t + base->spline->splines[major].c;
779     bdo = (3*base->spline->splines[other].a*t + 2*base->spline->splines[other].b)*t + base->spline->splines[other].c;
780 
781     if ( tdm==0 && bdm==0 )
782 return( tdo > bdo );
783     if ( tdo==0 )
784 return( tdo>0 );
785     else if ( bdo==0 )
786 return( bdo>0 );
787 
788 return( tdo/tdm > bdo/bdm );
789 }
790 
ELOrder(EIList * el,int major)791 void ELOrder(EIList *el, int major ) {
792     int other = !major;
793     int pos;
794     EI *ei, *prev, *test;
795 
796     el->low = floor(el->coordmin[major]); el->high = ceil(el->coordmax[major]);
797     el->cnt = el->high-el->low+1;
798     el->ordered = calloc(el->cnt,sizeof(EI *));
799     el->ends = calloc(el->cnt,1);
800 
801     for ( ei = el->edges; ei!=NULL ; ei=ei->next ) {
802 	pos = ceil(ei->coordmax[major])-el->low;
803 	el->ends[pos] = true;
804 	pos = floor(ei->coordmin[major])-el->low;
805 	ei->ocur = (ei->hup == ei->vup)?ei->coordmin[other]:ei->coordmax[other];
806 	ei->tcur = ((major && ei->vup) || (!major && ei->hup)) ?
807 			ei->tmin: ei->tmax;
808 	if ( major ) {
809 	    ei->up = ei->vup;
810 	    ei->hv = (ei->vert || ei->almostvert);
811 	    ei->hvbottom = ei->vup ? ei->vertattmin : ei->vertattmax;
812 	    ei->hvtop    =!ei->vup ? ei->vertattmin : ei->vertattmax;
813 	    if ( ei->hor || ei->almosthor)
814     continue;
815 	} else {
816 	    ei->up = ei->hup;
817 	    ei->hv = (ei->hor || ei->almosthor);
818 	    ei->hvbottom = ei->hup ? ei->horattmin : ei->horattmax;
819 	    ei->hvtop    =!ei->hup ? ei->horattmin : ei->horattmax;
820 	    if ( ei->vert || ei->almostvert)
821     continue;
822 	}
823 	if ( el->ordered[pos]==NULL || ei->ocur<el->ordered[pos]->ocur ) {
824 	    ei->ordered = el->ordered[pos];
825 	    el->ordered[pos] = ei;
826 	} else {
827 	    for ( prev=el->ordered[pos], test = prev->ordered; test!=NULL;
828 		    prev = test, test = test->ordered ) {
829 		if ( test->ocur>ei->ocur ||
830 			(test->ocur==ei->ocur && IsBiggerSlope(test,ei,major)))
831 	    break;
832 	    }
833 	    ei->ordered = test;
834 	    prev->ordered = ei;
835 	}
836     }
837 }
838 
HIMerge(HintInstance * into,HintInstance * hi)839 static HintInstance *HIMerge(HintInstance *into, HintInstance *hi) {
840     HintInstance *n, *first = NULL, *last = NULL;
841 
842     while ( into!=NULL && hi!=NULL ) {
843 	if ( into->begin<hi->begin ) {
844 	    n = into;
845 	    into = into->next;
846 	} else {
847 	    n = hi;
848 	    hi = hi->next;
849 	}
850 	if ( first==NULL )
851 	    first = n;
852 	else
853 	    last->next = n;
854 	last = n;
855     }
856     if ( into!=NULL ) {
857 	if ( first==NULL )
858 	    first = into;
859 	else
860 	    last->next = into;
861     } else if ( hi!=NULL ) {
862 	if ( first==NULL )
863 	    first = hi;
864 	else
865 	    last->next = hi;
866     }
867 return( first );
868 }
869 
HintCleanup(StemInfo * stem,int dosort,int instance_count)870 StemInfo *HintCleanup(StemInfo *stem,int dosort,int instance_count) {
871     StemInfo *s, *p=NULL, *t, *pt, *sn;
872     int swap;
873 
874     for ( s=stem; s!=NULL; p=s, s=s->next ) {
875 	if ( s->width<0 ) {
876 	    s->start += s->width;
877 	    s->width = -s->width;
878 	    s->ghost = true;
879 	}
880 	s->reordered = false;
881 	if ( p!=NULL && p->start> s->start )
882 	    dosort = true;
883     }
884     if ( dosort ) {
885 	for ( p=NULL, s=stem; s!=NULL; p=s, s = sn ) {
886 	    sn = s->next;
887 	    for ( pt=s, t=sn; t!=NULL; pt=t, t=t->next ) {
888 		if ( instance_count>1 &&
889 			t->u.unblended!=NULL && s->u.unblended!=NULL ) {
890 		    int temp = UnblendedCompare((*t->u.unblended)[0],(*s->u.unblended)[0],instance_count);
891 		    if ( temp==0 )
892 			swap = UnblendedCompare((*t->u.unblended)[1],(*s->u.unblended)[1],instance_count);
893 		    else
894 			swap = temp<0;
895 		} else if ( t->start<s->start )
896 		    swap=true;
897 		else if ( t->start>s->start )
898 		    swap = false;
899 		else
900 		    swap = (t->width<s->width);
901 		if ( swap ) {
902 		    s->next = t->next;
903 		    if ( pt==s ) {
904 			t->next = s;
905 			sn = s;
906 		    } else {
907 			t->next = sn;
908 			pt->next = s;
909 		    }
910 		    if ( p==NULL )
911 			stem = t;
912 		    else
913 			p->next = t;
914 		    pt = s;	/* swap s and t */
915 		    s = t;
916 		    t = pt;
917 		}
918 	    }
919 	}
920 	/* Remove duplicates */
921 	if ( stem!=NULL ) for ( p=stem, s=stem->next; s!=NULL; s = sn ) {
922 	    sn = s->next;
923 	    if ( p->start==s->start && p->width==s->width && p->hintnumber==s->hintnumber ) {
924 		p->where = HIMerge(p->where,s->where);
925 		s->where = NULL;
926 		p->next = sn;
927 		StemInfoFree(s);
928 	    } else
929 		p = s;
930 	}
931     }
932 return( stem );
933 }
934 
EITOfNextMajor(EI * e,EIList * el,real sought_m)935 real EITOfNextMajor(EI *e, EIList *el, real sought_m ) {
936     /* We want to find t so that Mspline(t) = sought_m */
937     /*  the curve is monotonic */
938     Spline1D *msp = &e->spline->splines[el->major];
939     real new_t;
940     real found_m;
941     real t_mmax, t_mmin;
942 
943     if ( msp->a==0 && msp->b==0 ) {
944 	if ( msp->c == 0 ) {
945 	    IError("Hor/Vert line when not expected");
946 return( 0 );
947 	}
948 	new_t = (sought_m-msp->d)/(msp->c);
949 return( new_t );
950     }
951 
952     t_mmax = e->up?e->tmax:e->tmin;
953     t_mmin = e->up?e->tmin:e->tmax;
954     /* sought_m += el->low; */
955 
956     while ( 1 ) {
957 	new_t = (t_mmin+t_mmax)/2;
958 	found_m = ( ((msp->a*new_t+msp->b)*new_t+msp->c)*new_t + msp->d );
959 	if ( found_m>sought_m-.001 && found_m<sought_m+.001 )
960 return( new_t );
961 	if ( found_m > sought_m ) {
962 	    t_mmax = new_t;
963 	} else {
964 	    t_mmin = new_t;
965 	}
966 	if ( t_mmax==t_mmin ) {
967 	    IError("EITOfNextMajor failed! on %s", el->sc!=NULL?el->sc->name:"Unknown" );
968 return( new_t );
969 	}
970     }
971 }
972 
EIActiveListReorder(EI * active,int * change)973 EI *EIActiveListReorder(EI *active,int *change) {
974     int any;
975     EI *pr, *apt;
976 
977     *change = false;
978     if ( active!=NULL ) {
979 	any = true;
980 	while ( any ) {
981 	    any = false;
982 	    for ( pr=NULL, apt=active; apt->aenext!=NULL; ) {
983 		if ( apt->ocur <= apt->aenext->ocur ) {
984 		    /* still ordered */;
985 		    pr = apt;
986 		    apt = apt->aenext;
987 		} else if ( pr==NULL ) {
988 		    active = apt->aenext;
989 		    apt->aenext = apt->aenext->aenext;
990 		    active->aenext = apt;
991 		    *change = true;
992 		    /* don't need to set any, since this reorder can't disorder the list */
993 		    pr = active;
994 		} else {
995 		    pr->aenext = apt->aenext;
996 		    apt->aenext = apt->aenext->aenext;
997 		    pr->aenext->aenext = apt;
998 		    any = *change = true;
999 		    pr = pr->aenext;
1000 		}
1001 	    }
1002 	}
1003     }
1004 return( active );
1005 }
1006 
EIActiveEdgesRefigure(EIList * el,EI * active,real i,int major,int * _change)1007 EI *EIActiveEdgesRefigure(EIList *el, EI *active,real i,int major, int *_change) {
1008     EI *apt, *pr, *npt;
1009     int change = false, subchange;
1010     int other = !major;
1011 
1012     /* first remove any entry which doesn't intersect the new scan line */
1013     /*  (ie. stopped on last line) */
1014     for ( pr=NULL, apt=active; apt!=NULL; apt = apt->aenext ) {
1015 	if ( apt->coordmax[major]<i+el->low ) {
1016 	    if ( pr==NULL )
1017 		active = apt->aenext;
1018 	    else
1019 		pr->aenext = apt->aenext;
1020 	    change = true;
1021 	} else
1022 	    pr = apt;
1023     }
1024     /* then move the active list to the next line */
1025     for ( apt=active; apt!=NULL; apt = apt->aenext ) {
1026 	Spline1D *osp = &apt->spline->splines[other];
1027 	apt->tcur = EITOfNextMajor(apt,el,i+el->low);
1028 	apt->ocur = ( ((osp->a*apt->tcur+osp->b)*apt->tcur+osp->c)*apt->tcur + osp->d );
1029     }
1030     /* reorder list */
1031     active = EIActiveListReorder(active,&subchange);
1032     if ( subchange ) change = true;
1033 
1034     /* Insert new nodes */
1035     if ( el->ordered[(int) i]!=NULL ) change = true;
1036     for ( pr=NULL, apt=active, npt=el->ordered[(int) i]; apt!=NULL && npt!=NULL; ) {
1037 	if ( npt->ocur<apt->ocur ) {
1038 	    npt->aenext = apt;
1039 	    if ( pr==NULL )
1040 		active = npt;
1041 	    else
1042 		pr->aenext = npt;
1043 	    pr = npt;
1044 	    npt = npt->ordered;
1045 	} else {
1046 	    pr = apt;
1047 	    apt = apt->aenext;
1048 	}
1049     }
1050     while ( npt!=NULL ) {
1051 	npt->aenext = NULL;
1052 	if ( pr==NULL )
1053 	    active = npt;
1054 	else
1055 	    pr->aenext = npt;
1056 	pr = npt;
1057 	npt = npt->ordered;
1058     }
1059     *_change = change;
1060 return( active );
1061 }
1062 
1063 /* Should I consider e and n to be a continuation of the same spline? */
1064 /*  If we are at an intersection (and it's the same intersection on both) */
1065 /*  and they go in vaguely the same direction then we should */
1066 /* Ah, but also if they are at different intersections and are connected */
1067 /*  by a series of horizontal/vertical lines (whichever are invisible to major)*/
1068 /*  then we still should. */
EISameLine(EI * e,EI * n,real i,int major)1069 int EISameLine(EI *e, EI *n, real i, int major) {
1070     EI *t;
1071 
1072     if ( n!=NULL && /*n->up==e->up &&*/
1073 	    (ceil(e->coordmin[major])==i || floor(e->coordmin[major])==i || floor(e->coordmax[major])==i || ceil(e->coordmax[major])==i) &&
1074 	    (ceil(n->coordmin[major])==i || floor(n->coordmin[major])==i || floor(n->coordmax[major])==i || ceil(n->coordmax[major])==i) ) {
1075 	if (
1076 	     (n==e->splinenext && n->tmin==e->tmax &&
1077 		    n->tcur<n->tmin+.2 && e->tcur>e->tmax-.2 ) ||
1078 	     (n->splinenext==e && n->tmax==e->tmin &&
1079 		    n->tcur>n->tmax-.2 && e->tcur<e->tmin+.2 ) )
1080 return( true );
1081 	/* can be separated by a horizontal/vertical line in the other direction */
1082 	if ( n->tmax==1 && e->tmin==0 && n->tcur>.8 && e->tcur<.2) {
1083 	    t = n;
1084 	    while ( (t = t->splinenext)!=e ) {
1085 		if ( t==NULL || t==n ||
1086 			(major && !t->hor) || ( !major && !t->vert ))
1087 return( false );
1088 	    }
1089 return( n->up==e->up );
1090 	} else if ( n->tmin==0 && e->tmax==1 && n->tcur<.2 && e->tcur>.8) {
1091 	    t = e;
1092 	    while ( (t = t->splinenext)!=n ) {
1093 		if ( t==NULL || t==e ||
1094 			(major && !t->hor) || ( !major && !t->vert ))
1095 return( false );
1096 	    }
1097 return( n->up==e->up );
1098 	}
1099     }
1100 return( false );
1101 }
1102 
EISkipExtremum(EI * e,real i,int major)1103 int EISkipExtremum(EI *e, real i, int major) {
1104     EI *n = e->aenext, *t;
1105 
1106     if ( n==NULL )
1107 return( false );
1108     if (
1109 	    (ceil(e->coordmin[major])==i || floor(e->coordmin[major])==i || floor(e->coordmax[major])==i || ceil(e->coordmax[major])==i) &&
1110 	    (ceil(n->coordmin[major])==i || floor(n->coordmin[major])==i || floor(n->coordmax[major])==i || ceil(n->coordmax[major])==i) ) {
1111 	if (
1112 	     (n==e->splinenext && n->tmin==e->tmax &&
1113 		    n->tcur<n->tmin+.2 && e->tcur>e->tmax-.2 ) ||
1114 	     (n->splinenext==e && n->tmax==e->tmin &&
1115 		    n->tcur>n->tmax-.2 && e->tcur<e->tmin+.2 ) )
1116 return( n->up!=e->up );
1117 	/* can be separated by a horizontal/vertical line in the other direction */
1118 	if ( n->tmax==1 && e->tmin==0 && n->tcur>.8 && e->tcur<.2) {
1119 	    t = n;
1120 	    while ( (t = t->splinenext)!=e ) {
1121 		if ( t==NULL || t==n ||
1122 			(major && !t->hor) || ( !major && !t->vert ))
1123 return( false );
1124 	    }
1125 return( n->up!=e->up );
1126 	} else if ( n->tmin==0 && e->tmax==1 && n->tcur<.2 && e->tcur>.8) {
1127 	    t = e;
1128 	    while ( (t = t->splinenext)!=n ) {
1129 		if ( t==NULL || t==e ||
1130 			(major && !t->hor) || ( !major && !t->vert ))
1131 return( false );
1132 	    }
1133 return( n->up!=e->up );
1134 	}
1135     }
1136 return( false );
1137 }
1138 
EIActiveEdgesFindStem(EI * apt,real i,int major)1139 EI *EIActiveEdgesFindStem(EI *apt, real i, int major) {
1140     int cnt=apt->up?1:-1;
1141     EI *e, *p;
1142 
1143 	/* If we're at an intersection point and the next spline continues */
1144 	/*  in about the same direction then this doesn't count as two lines */
1145 	/*  but as one */
1146     if ( EISameLine(apt,apt->aenext,i,major))
1147 	apt = apt->aenext;
1148 
1149     e=apt->aenext;
1150     if ( e==NULL )
1151 return( NULL );
1152 
1153     for ( ; e!=NULL && cnt!=0; e=e->aenext ) {
1154 	p = e;
1155 	if ( EISkipExtremum(e,i,major)) {
1156 	    e = e->aenext;
1157 	    if ( e==NULL )
1158     break;
1159     continue;
1160 	}
1161 	if ( EISameLine(e,e->aenext,i,major))
1162 	    e = e->aenext;
1163 	cnt += (e->up?1:-1);
1164     }
1165 return( p );
1166 }
1167 
StemRemoveFlexCandidates(StemInfo * stems)1168 static StemInfo *StemRemoveFlexCandidates(StemInfo *stems) {
1169     StemInfo *s, *t, *sn;
1170     const real BlueShift = 7;
1171     /* Suppose we have something that is a flex candidate */
1172     /* We might get two hints from it... one from the two end points */
1173     /* and one from the internal point */
1174 
1175     if ( stems==NULL )
1176 return( NULL );
1177 
1178     for ( s=stems; (sn = s->next)!=NULL; s = sn ) {
1179 	if ( s->start+BlueShift > sn->start && s->width>0 && sn->width>0 &&
1180 		s->start+s->width-BlueShift < sn->start+sn->width &&
1181 		s->start+s->width+BlueShift > sn->start+sn->width &&
1182                 s->where != NULL && sn->where != NULL &&
1183 		s->where->next!=NULL && sn->where->next==NULL ) {
1184 	    t = sn->next;
1185 	    sn->next = NULL;
1186 	    StemInfoFree(sn);
1187 	    s->next = t;
1188 	    sn = t;
1189 	    if ( t==NULL )
1190     break;
1191 	}
1192     }
1193 return( stems );
1194 }
1195 
HIlen(StemInfo * stems)1196 real HIlen( StemInfo *stems) {
1197     HintInstance *hi;
1198     real len = 0;
1199 
1200     for ( hi=stems->where; hi!=NULL; hi = hi->next )
1201 	len += hi->end-hi->begin;
1202 return( len );
1203 }
1204 
HIoverlap(HintInstance * mhi,HintInstance * thi)1205 real HIoverlap( HintInstance *mhi, HintInstance *thi) {
1206     HintInstance *hi;
1207     real len = 0;
1208     real s, e;
1209 
1210     for ( ; mhi!=NULL; mhi = mhi->next ) {
1211 	for ( hi = thi; hi!=NULL && hi->begin<=mhi->end; hi = hi->next ) {
1212 	    if ( hi->end<mhi->begin ) {
1213 		thi = hi;
1214 	continue;
1215 	    }
1216 	    s = hi->begin<mhi->begin?mhi->begin:hi->begin;
1217 	    e = hi->end>mhi->end?mhi->end:hi->end;
1218 	    if ( e<s )
1219 	continue;		/* Shouldn't happen */
1220 	    len += e-s;
1221 	}
1222     }
1223 return( len );
1224 }
1225 
StemInfoAnyOverlaps(StemInfo * stems)1226 int StemInfoAnyOverlaps(StemInfo *stems) {
1227     while ( stems!=NULL ) {
1228 	if ( stems->hasconflicts )
1229 return( true );
1230 	stems = stems->next;
1231     }
1232 return( false );
1233 }
1234 
StemListAnyConflicts(StemInfo * stems)1235 int StemListAnyConflicts(StemInfo *stems) {
1236     StemInfo *s;
1237     int any= false;
1238     double end;
1239 
1240     for ( s=stems; s!=NULL ; s=s->next )
1241 	s->hasconflicts = false;
1242     while ( stems!=NULL ) {
1243 	end = stems->width<0 ? stems->start : stems->start+stems->width;
1244 	for ( s=stems->next; s!=NULL && (s->width>0 ? s->start : s->start+s->width)<=end; s=s->next ) {
1245 	    stems->hasconflicts = true;
1246 	    s->hasconflicts = true;
1247 	    any = true;
1248 	}
1249 	stems = stems->next;
1250     }
1251 return( any );
1252 }
1253 
HICopyTrans(HintInstance * hi,real mul,real offset)1254 HintInstance *HICopyTrans(HintInstance *hi, real mul, real offset) {
1255     HintInstance *first=NULL, *last, *cur, *p;
1256 
1257     while ( hi!=NULL ) {
1258 	cur = chunkalloc(sizeof(HintInstance));
1259 	if ( mul>0 ) {
1260 	    cur->begin = hi->begin*mul+offset;
1261 	    cur->end = hi->end*mul+offset;
1262 	    if ( first==NULL )
1263 		first = cur;
1264 	    else
1265 		last->next = cur;
1266 	    last = cur;
1267 	} else {
1268 	    cur->begin = hi->end*mul+offset;
1269 	    cur->end = hi->begin*mul+offset;
1270 	    if ( first==NULL || cur->begin<first->begin ) {
1271 		cur->next = first;
1272 		first = cur;
1273 	    } else {
1274 		for ( p=first, last=p->next; last!=NULL && cur->begin>last->begin; last=last->next );
1275 		p->next = cur;
1276 		cur->next = last;
1277 	    }
1278 	}
1279 	hi = hi->next;
1280     }
1281 return( first );
1282 }
1283 
SCGuessHintPoints(SplineChar * sc,int layer,StemInfo * stem,int major,int off)1284 static HintInstance *SCGuessHintPoints(SplineChar *sc, int layer, StemInfo *stem,int major, int off) {
1285     SplinePoint *starts[20], *ends[20];
1286     int spt=0, ept=0;
1287     SplinePointList *spl;
1288     SplinePoint *sp, *np;
1289     int sm, wm, i, j;
1290     unsigned val;
1291     real coord;
1292     HintInstance *head, *test, *cur, *prev;
1293 
1294     for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
1295 	for ( sp=spl->first; ; sp = np ) {
1296 	    coord = (major?sp->me.x:sp->me.y);
1297 	    sm = coord>=stem->start-off && coord<=stem->start+off;
1298 	    wm = coord>=stem->start+stem->width-off && coord<=stem->start+stem->width+off;
1299 	    if ( sm && spt<20 )
1300 		starts[spt++] = sp;
1301 	    if ( wm && ept<20 )
1302 		ends[ept++] = sp;
1303 	    if ( sp->next==NULL )
1304 	break;
1305 	    np = sp->next->to;
1306 	    if ( np==spl->first )
1307 	break;
1308 	}
1309     }
1310 
1311     head = NULL;
1312     for ( i=0; i<spt; ++i ) {
1313 	val = 0x80000000;
1314 	for ( j=0; j<ept; ++j ) {
1315 	    if ( major && starts[i]->me.y>=ends[j]->me.y-1 && starts[i]->me.y<=ends[j]->me.y+1 ) {
1316 		val = starts[i]->me.y;
1317 	break;
1318 	    } else if ( !major && starts[i]->me.x>=ends[j]->me.x-1 && starts[i]->me.x<=ends[j]->me.x+1 ) {
1319 		val = starts[i]->me.x;
1320 	break;
1321 	    }
1322 	}
1323 	if ( val!=0x80000000 ) {
1324 	    for ( prev=NULL, test=head; test!=NULL && val>test->begin; prev=test, test=test->next );
1325 	    if ( test==NULL || val!=test->begin ) {
1326 		cur = chunkalloc(sizeof(HintInstance));
1327 		cur->begin = cur->end = val;
1328 		cur->next = test;
1329 		if ( prev==NULL ) head = cur;
1330 		else prev->next = cur;
1331 	    }
1332 	}
1333     }
1334 return( head );
1335 }
1336 
StemAddHIFromActive(struct stemdata * stem,int major)1337 static HintInstance *StemAddHIFromActive(struct stemdata *stem,int major) {
1338     int i;
1339     HintInstance *head = NULL, *cur, *t;
1340     double mino, maxo;
1341     double dir = ((real *) &stem->unit.x)[major]<0 ? -1 : 1;
1342 
1343     for ( i=0; i<stem->activecnt; ++i ) {
1344 	mino = dir*stem->active[i].start + ((real *) &stem->left.x)[major];
1345 	maxo = dir*stem->active[i].end + ((real *) &stem->left.x)[major];
1346 	cur = chunkalloc(sizeof(HintInstance));
1347 	if ( dir>0 ) {
1348 	    cur->begin = mino;
1349 	    cur->end = maxo;
1350 	    if ( head==NULL )
1351 		head = cur;
1352 	    else
1353 		t->next = cur;
1354 	    t = cur;
1355 	} else {
1356 	    cur->begin = maxo;
1357 	    cur->end = mino;
1358 	    cur->next = head;
1359 	    head = cur;
1360 	}
1361     }
1362 return( head );
1363 }
1364 
DStemAddHIFromActive(struct stemdata * stem)1365 static HintInstance *DStemAddHIFromActive( struct stemdata *stem ) {
1366     int i;
1367     HintInstance *head = NULL, *cur, *t;
1368 
1369     for ( i=0; i<stem->activecnt; ++i ) {
1370         cur = chunkalloc( sizeof( HintInstance ));
1371 	cur->begin = stem->active[i].start;
1372 	cur->end = stem->active[i].end;
1373 	if ( head == NULL )
1374 	    head = cur;
1375 	else
1376 	    t->next = cur;
1377 	t = cur;
1378     }
1379 return( head );
1380 }
1381 
SCGuessHVHintInstances(SplineChar * sc,int layer,StemInfo * si,int is_v)1382 static void SCGuessHVHintInstances( SplineChar *sc,int layer,StemInfo *si,int is_v ) {
1383     struct glyphdata *gd;
1384     struct stemdata *sd;
1385     double em_size = ( sc->parent != NULL ) ?
1386         sc->parent->ascent + sc->parent->descent : 1000;
1387 
1388     gd = GlyphDataInit( sc,layer,em_size,true );
1389     if ( gd == NULL )
1390 return;
1391     StemInfoToStemData( gd,si,is_v );
1392     if ( gd->stemcnt > 0 ) {
1393         sd = &gd->stems[0];
1394         si->where = StemAddHIFromActive( sd,is_v );
1395     }
1396     GlyphDataFree( gd );
1397 }
1398 
SCGuessHintInstancesLight(SplineChar * sc,int layer,StemInfo * stem,int major)1399 static void SCGuessHintInstancesLight(SplineChar *sc, int layer, StemInfo *stem,int major) {
1400     SplinePointList *spl;
1401     SplinePoint *sp, *np;
1402     int sm, wm, off;
1403     real ob = 0.0, oe = 0.0;
1404     HintInstance *s=NULL, *w=NULL, *cur, *p, *t, *n, *w2;
1405     /* We've got a hint (from somewhere, old data, reading in a font, user specified etc.) */
1406     /*  but we don't have HintInstance info. So see if we can find those data */
1407     /* Will get confused by stems with holes in them (for example if you make */
1408     /*  a hint from one side of an H to the other, it will get the whole thing */
1409     /*  not just the cross stem) */
1410 
1411     for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
1412 	for ( sp=spl->first; ; sp = np ) {
1413 
1414 	    float mexy = (major ? sp->me.x : sp->me.y);
1415 	    sm = equalWithTolerence( mexy, stem->start, OpenTypeLoadHintEqualityTolerance );
1416 	    wm = equalWithTolerence( mexy, stem->start+stem->width, OpenTypeLoadHintEqualityTolerance );
1417 
1418 	    if ( sp->next==NULL )
1419 	break;
1420 	    np = sp->next->to;
1421 	    if ( sm || wm ) {
1422 		if ( !major ) {
1423 		    if ( np->me.y==sp->me.y ) {
1424 			ob = sp->me.x; oe = np->me.x;
1425 		    } else if ( sp->nextcp.y==sp->me.y ) {
1426 			ob = sp->me.x; oe = (sp->me.x+sp->nextcp.x)/2;
1427 			if ( sp->prevcp.y==sp->me.y )
1428 			    ob = (sp->prevcp.x+sp->me.x)/2;
1429 		    } else if ( sp->prevcp.y==sp->me.y ) {
1430 			ob = sp->me.x; oe = (sp->prevcp.x+sp->me.x)/2;
1431 		    } else
1432 			sm = wm = false;
1433 		} else {
1434 		    if ( np->me.x==sp->me.x ) {
1435 			ob = sp->me.y; oe = np->me.y;
1436 		    } else if ( sp->nextcp.x==sp->me.x ) {
1437 			ob = sp->me.y; oe = (sp->nextcp.y+sp->me.y)/2;
1438 			if ( sp->prevcp.x==sp->me.x )
1439 			    ob = (sp->prevcp.y+sp->me.y)/2;
1440 		    } else if ( sp->prevcp.x==sp->me.x ) {
1441 			ob = sp->me.y; oe = (sp->prevcp.y+sp->me.y)/2;
1442 		    } else
1443 			sm = wm = false;
1444 		}
1445 	    }
1446 	    if ( sm || wm ) {
1447 		cur = chunkalloc(sizeof(HintInstance));
1448 		if ( ob>oe ) { real temp=ob; ob=oe; oe=temp;}
1449 		cur->begin = ob;
1450 		cur->end = oe;
1451 		if ( sm ) {
1452 		    if ( s==NULL || s->begin>cur->begin ) {
1453 			cur->next = s;
1454 			s = cur;
1455 		    } else {
1456 			p = s;
1457 			for ( t=s->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next );
1458 			p->next = cur; cur->next = t;
1459 		    }
1460 		} else {
1461 		    if ( w==NULL || w->begin>cur->begin ) {
1462 			cur->next = w;
1463 			w = cur;
1464 		    } else {
1465 			p = w;
1466 			for ( t=w->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next );
1467 			p->next = cur; cur->next = t;
1468 		    }
1469 		}
1470 	    }
1471 	    if ( np==spl->first )
1472 	break;
1473 	}
1474     }
1475 
1476     /* Now we know what the right side of the stem looks like, and we know */
1477     /*  what the left side looks like. They may not look the same (H for example) */
1478     /*  Figure out the set where both are active */
1479     /* Unless it's a ghost hint */
1480     if ( stem->width==20 && s==NULL && w!=NULL ) {
1481 	s = w;
1482 	w = NULL;
1483     } else if ( stem->width==21 && s!=NULL && w==NULL) {
1484 	/* Just use s */;
1485     } else for ( p=NULL, t=s; t!=NULL; t=n ) {
1486 	n = t->next;
1487 	for ( w2=w; w2!=NULL && w2->begin<t->end ; w2=w2->next ) {
1488 	    if ( w2->end<=t->begin )
1489 		continue;
1490 	    if ( w2->begin<=t->begin && w2->end>=t->end ) {
1491 		/* Perfect match */
1492 		break;
1493 	    }
1494 	    if ( w2->begin>=t->begin )
1495 		t->begin = w2->begin;
1496 	    if ( w2->end<=t->end ) {
1497 		cur = chunkalloc(sizeof(HintInstance));
1498 		cur->begin = w2->end;
1499 		cur->end = t->end;
1500 		cur->next = n;
1501 		t->next = cur;
1502 		n = cur;
1503 		t->end = w2->end;
1504 	    }
1505 	    break;
1506 	}
1507 	if ( w2!=NULL && w2->begin>=t->end )
1508 	    w2 = NULL;
1509 	if ( w2==NULL && w!=NULL ) {
1510 	    HintInstance *best = NULL;
1511 	    double best_off=1e10, off;
1512 	    for ( w2=w; w2!=NULL ; w2=w2->next ) {
1513 		if ( w2->end<=t->begin )
1514 		    off = t->begin - w2->end;
1515 		else
1516 		    off = w2->begin - t->end;
1517 		if ( best==NULL && off<best_off ) {
1518 		    best = w2;
1519 		    best_off = off;
1520 		}
1521 	    }
1522 	    if ( best!=NULL && best_off<stem->width ) {
1523 		w2 = best;
1524 		if( w2->begin<t->begin )
1525 		    t->begin = w2->begin;
1526 		if ( w2->end>t->end )
1527 		    t->end = w2->end;
1528 	    }
1529 	}
1530 	if ( w2==NULL ) {
1531 	    /* No match for t (or if there were it wasn't complete) get rid */
1532 	    /*  of what's left of t */
1533 	    chunkfree(t,sizeof(*t));
1534 	    if ( p==NULL )
1535 		s = n;
1536 	    else
1537 		p->next = n;
1538 	} else
1539 	    p = t;
1540     }
1541     while ( w!=NULL ) {
1542 	n = w->next;
1543 	chunkfree(w,sizeof(*w));
1544 	w=n;
1545     }
1546 
1547     /* If we couldn't find anything, then see if there are two points which */
1548     /*  have the same x or y value and whose other coordinates match those of */
1549     /*  the hint */
1550     /* Surprisingly many fonts have hints which don't accurately match the */
1551     /*  points. Perhaps BlueFuzz (default 1) applies here too */
1552     for ( off=0; off<1 && s==NULL; ++off )
1553 	s = SCGuessHintPoints(sc,layer, stem,major,off);
1554 
1555     stem->where = s;
1556 }
1557 
StemInfoAdd(StemInfo * list,StemInfo * new)1558 static StemInfo *StemInfoAdd(StemInfo *list, StemInfo *new) {
1559     StemInfo *prev, *test;
1560 
1561     for ( prev=NULL, test=list; test!=NULL && new->start>test->start; prev=test, test=test->next );
1562     if ( test!=NULL && test->start==new->start && test->width==new->width ) {
1563 	/* Replace the old with the new */
1564 	/* can't just free the new, because the Guess routines depend on it */
1565 	/*  being around */
1566 	new->next = test->next;
1567 	StemInfoFree(test);
1568     } else
1569 	new->next = test;
1570     if ( prev==NULL )
1571 	list = new;
1572     else
1573 	prev->next = new;
1574 return( list );
1575 }
1576 
SCGuessHintInstancesList(SplineChar * sc,int layer,StemInfo * hstem,StemInfo * vstem,DStemInfo * dstem,int hvforce,int dforce)1577 void SCGuessHintInstancesList( SplineChar *sc,int layer,StemInfo *hstem,StemInfo *vstem,DStemInfo *dstem,
1578     int hvforce,int dforce ) {
1579 
1580     struct glyphdata *gd;
1581     struct stemdata *sd;
1582     int i, cnt=0, hneeds_gd=false, vneeds_gd=false, dneeds_gd=false;
1583     StemInfo *test;
1584     DStemInfo *dtest;
1585     double em_size = ( sc->parent != NULL ) ?
1586         sc->parent->ascent + sc->parent->descent : 1000;
1587 
1588     if ( hstem == NULL && vstem == NULL && dstem == NULL )
1589 return;
1590     /* If all stems already have active zones assigned (actual for .sfd */
1591     /* files), then there is no need to wast time generating glyph data for */
1592     /* this glyph */
1593     test = hstem;
1594     while ( !hneeds_gd && test != NULL ) {
1595         if ( test->where == NULL || hvforce ) hneeds_gd = true;
1596         test = test->next;
1597     }
1598     test = vstem;
1599     while ( !vneeds_gd && test != NULL ) {
1600         if ( test->where == NULL || hvforce ) vneeds_gd = true;
1601         test = test->next;
1602     }
1603     dtest = dstem;
1604     while ( !dneeds_gd && dtest != NULL ) {
1605         if ( dtest->where == NULL || dforce ) dneeds_gd = true;
1606         dtest = dtest->next;
1607     }
1608     if ( !hneeds_gd && !vneeds_gd && !dneeds_gd )
1609 return;
1610 
1611     gd = GlyphDataInit( sc,layer,em_size,!dneeds_gd );
1612     if ( gd == NULL )
1613 return;
1614 
1615     cnt = 0;
1616     if ( hstem != NULL && hneeds_gd ) {
1617         gd = StemInfoToStemData( gd,hstem,false );
1618         for ( i=cnt; i<gd->stemcnt; i++ ) {
1619             sd = &gd->stems[i];
1620             if ( hstem == NULL )
1621         break;
1622             if ( hstem->where == NULL || hvforce )
1623                 hstem->where = StemAddHIFromActive( sd,false );
1624             hstem = hstem->next;
1625         }
1626     }
1627     cnt = gd->stemcnt;
1628     if ( vstem != NULL && vneeds_gd ) {
1629         gd = StemInfoToStemData( gd,vstem,true );
1630         for ( i=cnt; i<gd->stemcnt; i++ ) {
1631             sd = &gd->stems[i];
1632             if ( vstem == NULL )
1633         break;
1634             if ( vstem->where == NULL || hvforce )
1635                 vstem->where = StemAddHIFromActive( sd,true );
1636             vstem = vstem->next;
1637         }
1638     }
1639     cnt = gd->stemcnt;
1640     if ( dstem != NULL && dneeds_gd ) {
1641         gd = DStemInfoToStemData( gd,dstem );
1642         for ( i=cnt; i<gd->stemcnt; i++ ) {
1643             sd = &gd->stems[i];
1644             if ( dstem == NULL )
1645         break;
1646             dstem->left = sd->left; dstem->right = sd->right;
1647             if ( dstem->where == NULL || dforce )
1648                 dstem->where = DStemAddHIFromActive( sd );
1649             dstem = dstem->next;
1650         }
1651     }
1652     GlyphDataFree( gd );
1653 return;
1654 }
1655 
SCGuessDHintInstances(SplineChar * sc,int layer,DStemInfo * ds)1656 void SCGuessDHintInstances( SplineChar *sc, int layer, DStemInfo *ds ) {
1657     struct glyphdata *gd;
1658     struct stemdata *sd;
1659     double em_size = ( sc->parent != NULL ) ?
1660         sc->parent->ascent + sc->parent->descent : 1000;
1661 
1662     gd = GlyphDataInit( sc,layer,em_size,false );
1663     if ( gd == NULL )
1664 return;
1665     DStemInfoToStemData( gd,ds );
1666     if ( gd->stemcnt > 0 ) {
1667         sd = &gd->stems[0];
1668         ds->left = sd->left; ds->right = sd->right;
1669         ds->where = DStemAddHIFromActive( sd );
1670 	if ( ds->where==NULL )
1671 	    IError( "Couldn't figure out where this hint is active" );
1672     }
1673     GlyphDataFree( gd );
1674 }
1675 
SCGuessHHintInstancesAndAdd(SplineChar * sc,int layer,StemInfo * stem,real guess1,real guess2)1676 void SCGuessHHintInstancesAndAdd(SplineChar *sc, int layer, StemInfo *stem, real guess1, real guess2) {
1677     SCGuessHVHintInstances( sc,layer,stem,0 );
1678     sc->hstem = StemInfoAdd(sc->hstem,stem);
1679     if ( stem->where==NULL && guess1!=0x80000000 ) {
1680 	if ( guess1>guess2 ) { real temp = guess1; guess1 = guess2; guess2 = temp; }
1681 	stem->where = chunkalloc(sizeof(HintInstance));
1682 	stem->where->begin = guess1;
1683 	stem->where->end = guess2;
1684     }
1685     sc->hconflicts = StemListAnyConflicts(sc->hstem);
1686     if ( stem->hasconflicts ) {
1687 	/*StemInfoReduceOverlap(sc->hstem,stem);*/	/* User asked for it, assume s/he knows what s/he's doing */
1688 	if ( stem->where==NULL )
1689 	    IError("Couldn't figure out where this hint is active");
1690     }
1691 }
1692 
SCGuessVHintInstancesAndAdd(SplineChar * sc,int layer,StemInfo * stem,real guess1,real guess2)1693 void SCGuessVHintInstancesAndAdd(SplineChar *sc, int layer,StemInfo *stem, real guess1, real guess2) {
1694     SCGuessHVHintInstances( sc,layer,stem,1 );
1695     sc->vstem = StemInfoAdd(sc->vstem,stem);
1696     if ( stem->where==NULL && guess1!=0x80000000 ) {
1697 	if ( guess1>guess2 ) { real temp = guess1; guess1 = guess2; guess2 = temp; }
1698 	stem->where = chunkalloc(sizeof(HintInstance));
1699 	stem->where->begin = guess1;
1700 	stem->where->end = guess2;
1701     }
1702     sc->vconflicts = StemListAnyConflicts(sc->vstem);
1703     if ( stem->hasconflicts ) {
1704 	/*StemInfoReduceOverlap(sc->vstem,stem);*/	/* User asked for it, assume s/he knows what s/he's doing */
1705 	if ( stem->where==NULL )
1706 	    IError("Couldn't figure out where this hint is active");
1707     }
1708 }
1709 
SCGuessHHintInstancesList(SplineChar * sc,int layer)1710 void SCGuessHHintInstancesList(SplineChar *sc,int layer) {
1711     StemInfo *h;
1712     for ( h= sc->hstem; h!=NULL; h=h->next ) {
1713 	if ( h->where==NULL ) {
1714 	    SCGuessHintInstancesLight( sc,layer,h,false );
1715 	}
1716     }
1717 }
1718 
SCGuessVHintInstancesList(SplineChar * sc,int layer)1719 void SCGuessVHintInstancesList(SplineChar *sc,int layer) {
1720     StemInfo *h;
1721     for ( h= sc->vstem; h!=NULL; h=h->next ) {
1722 	if ( h->where==NULL ) {
1723 	    SCGuessHintInstancesLight( sc,layer,h,true );
1724 	}
1725     }
1726 }
1727 
1728 /* We have got (either from a file or user specified) a diagonal stem,
1729    described by 4 base points (pairs of x and y coordinates). Some additional
1730    tests are required before we can add this stem to the given glyph. */
MergeDStemInfo(SplineFont * sf,DStemInfo ** ds,DStemInfo * test)1731 int MergeDStemInfo( SplineFont *sf,DStemInfo **ds,DStemInfo *test ) {
1732     DStemInfo *dn, *cur, *prev, *next, *temp;
1733     double dot, loff, roff, soff, dist_error_diag ;
1734     double ibegin, iend;
1735     int overlap;
1736     BasePoint *base, *nbase, *pbase;
1737     HintInstance *hi;
1738 
1739     if ( *ds == NULL ) {
1740         *ds = test;
1741 return( true );
1742     }
1743     dist_error_diag = ( sf->ascent + sf->descent ) * 0.0065;
1744 
1745     cur = prev = NULL;
1746     for ( dn=*ds ; dn!=NULL ; dn=dn->next ) {
1747         prev = cur; cur = dn;
1748 
1749         /* Compare the given stem with each of the existing diagonal stem
1750          * hints. First ensure that it is not an exact duplicate of an already
1751          * added stem. Then test if unit vectors are parallel and edges colinear.
1752          * In this case we should either preserve the existing stem or replace
1753          * it with the new one, but not keep them both */
1754         if (test->unit.x == dn->unit.x && test->unit.y == dn->unit.y &&
1755             test->left.x == dn->left.x && test->left.y == dn->left.y &&
1756             test->right.x == dn->right.x && test->right.y == dn->right.y ) {
1757             DStemInfoFree( test );
1758 return( false );
1759         }
1760         dot = ( test->unit.x * dn->unit.y ) -
1761               ( test->unit.y * dn->unit.x );
1762         if ( dot <= -0.5 || dot >= 0.5 )
1763     continue;
1764 
1765         loff =  ( test->left.x - dn->left.x ) * dn->unit.y -
1766                 ( test->left.y - dn->left.y ) * dn->unit.x;
1767         roff =  ( test->right.x - dn->right.x ) * dn->unit.y -
1768                 ( test->right.y - dn->right.y ) * dn->unit.x;
1769         if (loff <= -dist_error_diag || loff >= dist_error_diag ||
1770             roff <= -dist_error_diag || roff >= dist_error_diag )
1771     continue;
1772         soff =  ( test->left.x - dn->left.x ) * dn->unit.x +
1773                 ( test->left.y - dn->left.y ) * dn->unit.y;
1774         overlap = false;
1775         if ( dn->where != NULL && test->where != NULL && test->where->next == NULL ) {
1776             ibegin = test->where->begin + soff;
1777             iend = test->where->end + soff;
1778             for ( hi = dn->where; hi != NULL; hi = hi->next ) {
1779                 if (( hi->begin <= ibegin && ibegin <= hi->end ) ||
1780                     ( hi->begin <= iend && iend <= hi->end ) ||
1781                     ( ibegin <= hi->begin && hi->end <= iend )) {
1782                     overlap = true;
1783                     break;
1784                 }
1785             }
1786         } else
1787             overlap = true;
1788         /* It's probably a colinear dstem, as in older SFD files. Treat */
1789         /* it as one more instance for the already added stem */
1790         if ( !overlap ) {
1791             for ( hi=dn->where; hi->next != NULL; hi = hi->next ) ;
1792             hi->next = chunkalloc( sizeof( HintInstance ));
1793             hi->next->begin = ibegin; hi->next->end = iend;
1794             DStemInfoFree( test );
1795 return( false );
1796         /* The found stem is close but not identical to the stem we  */
1797         /* are going to add. So just replace the older stem with the */
1798         /* new one */
1799         } else {
1800             test->next = dn->next;
1801             if ( prev == NULL )
1802                 *ds = test;
1803             else
1804                 prev->next = test;
1805             DStemInfoFree( dn );
1806 return( true );
1807         }
1808     }
1809 
1810     /* Insert the given stem to the list by such a way that diagonal
1811      * stems are ordered by the X coordinate of the left edge key point, and
1812      * by Y if X is the same. The order is arbitrary, but may be essential for
1813      * things like "W". So we should be sure that the autoinstructor will
1814      * process diagonals from left to right. */
1815     base = ( test->unit.y < 0 ) ? &test->right : &test->left;
1816     nbase = ( (*ds)->unit.y < 0 ) ? &(*ds)->right : &(*ds)->left;
1817 
1818     if ( base->x < nbase->x || ( base->x == nbase->x && base->y >= nbase->y )) {
1819         temp = *ds; *ds = test;
1820         (*ds)->next = temp;
1821     } else {
1822         for ( dn=*ds ; dn!=NULL && dn!=test ; dn=dn->next ) {
1823             next = dn->next;
1824             pbase = ( dn->unit.y < 0 ) ? &dn->right : &dn->left;
1825             if ( next != NULL )
1826                 nbase = ( next->unit.y < 0 ) ? &next->right : &next->left;
1827 
1828             if (( pbase->x < base->x ||
1829                 ( pbase->x == base->x && pbase->y >= base->y )) &&
1830                 ( next == NULL || base->x < nbase->x ||
1831                 ( base->x == nbase->x && base->y >= nbase->y ))) {
1832 
1833                 test->next = next; dn->next = test;
1834         break;
1835             }
1836 
1837         }
1838     }
1839 return( true );
1840 }
1841 
RefHintsMerge(StemInfo * into,StemInfo * rh,real mul,real offset,real omul,real oofset)1842 static StemInfo *RefHintsMerge(StemInfo *into, StemInfo *rh, real mul, real offset,
1843 	real omul, real oofset) {
1844     StemInfo *prev, *h, *n;
1845     real start, width;
1846 
1847     for ( ; rh!=NULL; rh=rh->next ) {
1848 	start = rh->start*mul + offset;
1849 	width = rh->width *mul;
1850 	if ( width<0 ) {
1851 	    start += width; width = -width;
1852 	}
1853 	for ( h=into, prev=NULL; h!=NULL && (start>h->start || (start==h->start && width>h->width)); prev=h, h=h->next );
1854 	if ( h==NULL || start!=h->start || width!=h->width ) {
1855 	    n = chunkalloc(sizeof(StemInfo));
1856 	    n->start = start; n->width = width;
1857 	    n->ghost = rh->ghost;
1858 	    n->next = h;
1859 	    if ( prev==NULL )
1860 		into = n;
1861 	    else
1862 		prev->next = n;
1863 	    n->where = HICopyTrans(rh->where,omul,oofset);
1864 	} else
1865 	    h->where = HIMerge(h->where,HICopyTrans(rh->where,omul,oofset));
1866     }
1867 return( into );
1868 }
1869 
RefDHintsMerge(SplineFont * sf,DStemInfo * into,DStemInfo * rh,real xmul,real xoffset,real ymul,real yoffset)1870 static DStemInfo *RefDHintsMerge( SplineFont *sf,DStemInfo *into,DStemInfo *rh,
1871     real xmul,real xoffset,real ymul,real yoffset ) {
1872     DStemInfo *new;
1873     double dmul;
1874 
1875     for ( ; rh!=NULL; rh=rh->next ) {
1876 	new = chunkalloc( sizeof( DStemInfo ));
1877 	*new = *rh;
1878 	new->left.x = xmul*new->left.x + xoffset;
1879 	new->right.x = xmul*new->right.x + xoffset;
1880 	new->left.y = ymul*new->left.y + yoffset;
1881 	new->right.y = ymul*new->right.y + yoffset;
1882         new->next = NULL;
1883 	if (( xmul < 0 && ymul > 0 ) || ( xmul > 0 && ymul < 0 ))
1884 	    new->unit.y = -new->unit.y;
1885         new->unit.x *= fabs( xmul ); new->unit.y *= fabs( ymul );
1886         dmul = sqrt( pow( new->unit.x,2 ) + pow( new->unit.y,2 ));
1887         new->unit.x /= dmul; new->unit.y /= dmul;
1888         if ( xmul < 0 ) dmul = -dmul;
1889         new->where = HICopyTrans( rh->where,dmul,0 );
1890 
1891 	MergeDStemInfo( sf,&into,new );
1892     }
1893 return( into );
1894 }
1895 
1896 static void __SplineCharAutoHint( SplineChar *sc, int layer, BlueData *bd, int gen_undoes );
1897 
AutoHintRefs(SplineChar * sc,int layer,BlueData * bd,int picky,int gen_undoes)1898 static void AutoHintRefs(SplineChar *sc,int layer, BlueData *bd, int picky, int gen_undoes) {
1899     RefChar *ref;
1900 
1901     /* Add hints for base characters before accent hints => if there are any */
1902     /*  conflicts, the base characters win */
1903     for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
1904 	if ( ref->transform[1]==0 && ref->transform[2]==0 ) {
1905 	    if ( picky ) {
1906 		if ( !ref->sc->manualhints && ref->sc->changedsincelasthinted &&
1907 			(ref->sc->layers[layer].refs!=NULL &&
1908 			 ref->sc->layers[layer].splines==NULL))
1909 		    AutoHintRefs(ref->sc,layer,bd,true,gen_undoes);
1910 	    } else if ( !ref->sc->manualhints && ref->sc->changedsincelasthinted )
1911 		__SplineCharAutoHint(ref->sc,layer,bd,gen_undoes);
1912 	    if ( ref->sc->unicodeenc!=-1 && ref->sc->unicodeenc<0x10000 &&
1913 		    isalnum(ref->sc->unicodeenc) ) {
1914 		sc->hstem = RefHintsMerge(sc->hstem,ref->sc->hstem,ref->transform[3], ref->transform[5], ref->transform[0], ref->transform[4]);
1915 		sc->vstem = RefHintsMerge(sc->vstem,ref->sc->vstem,ref->transform[0], ref->transform[4], ref->transform[3], ref->transform[5]);
1916 		sc->dstem = RefDHintsMerge(sc->parent,sc->dstem,ref->sc->dstem,ref->transform[0], ref->transform[4], ref->transform[3], ref->transform[5]);
1917 	    }
1918 	}
1919     }
1920 
1921     for ( ref=sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
1922 	if ( ref->transform[1]==0 && ref->transform[2]==0 &&
1923 		(ref->sc->unicodeenc==-1 || ref->sc->unicodeenc>=0x10000 ||
1924 			!isalnum(ref->sc->unicodeenc)) ) {
1925 	    sc->hstem = RefHintsMerge(sc->hstem,ref->sc->hstem,ref->transform[3], ref->transform[5], ref->transform[0], ref->transform[4]);
1926 	    sc->vstem = RefHintsMerge(sc->vstem,ref->sc->vstem,ref->transform[0], ref->transform[4], ref->transform[3], ref->transform[5]);
1927 	    sc->dstem = RefDHintsMerge(sc->parent,sc->dstem,ref->sc->dstem,ref->transform[0], ref->transform[4], ref->transform[3], ref->transform[5]);
1928 	}
1929     }
1930 
1931     sc->vconflicts = StemListAnyConflicts(sc->vstem);
1932     sc->hconflicts = StemListAnyConflicts(sc->hstem);
1933 
1934     SCOutOfDateBackground(sc);
1935     SCHintsChanged(sc);
1936 }
1937 
SCClearHints(SplineChar * sc)1938 void SCClearHints(SplineChar *sc) {
1939     int any = sc->hstem!=NULL || sc->vstem!=NULL || sc->dstem!=NULL;
1940     int layer;
1941 
1942     for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
1943 	SCClearHintMasks(sc,layer,true);
1944 	SCClearRounds(sc,layer);
1945     }
1946     StemInfosFree(sc->hstem);
1947     StemInfosFree(sc->vstem);
1948     sc->hstem = sc->vstem = NULL;
1949     sc->hconflicts = sc->vconflicts = false;
1950     DStemInfosFree(sc->dstem);
1951     sc->dstem = NULL;
1952     MinimumDistancesFree(sc->md);
1953     sc->md = NULL;
1954     SCOutOfDateBackground(sc);
1955     if ( any )
1956 	SCHintsChanged(sc);
1957 }
1958 
_SCClearHintMasks(SplineChar * sc,int layer,int counterstoo)1959 static void _SCClearHintMasks(SplineChar *sc,int layer, int counterstoo) {
1960     SplineSet *spl;
1961     SplinePoint *sp;
1962     RefChar *ref;
1963 
1964     if ( layer<0 || layer>=sc->layer_cnt )
1965         return;
1966 
1967     if ( counterstoo ) {
1968 	free(sc->countermasks);
1969 	sc->countermasks = NULL; sc->countermask_cnt = 0;
1970     }
1971 
1972     for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
1973 	for ( sp = spl->first ; ; ) {
1974 	    chunkfree(sp->hintmask,sizeof(HintMask));
1975 	    sp->hintmask = NULL;
1976 	    if ( sp->next==NULL )
1977 	break;
1978 	    sp = sp->next->to;
1979 	    if ( sp==spl->first )
1980 	break;
1981 	}
1982     }
1983 
1984     for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
1985 	for ( spl = ref->layers[0].splines; spl!=NULL; spl=spl->next ) {
1986 	    for ( sp = spl->first ; ; ) {
1987 		chunkfree(sp->hintmask,sizeof(HintMask));
1988 		sp->hintmask = NULL;
1989 		if ( sp->next==NULL )
1990 	    break;
1991 		sp = sp->next->to;
1992 		if ( sp==spl->first )
1993 	    break;
1994 	    }
1995 	}
1996     }
1997 }
1998 
ModifyHintMaskAdd(HintMask * hm,int index)1999 static void ModifyHintMaskAdd(HintMask *hm,int index) {
2000     int i;
2001 
2002     if ( hm==NULL )
2003 return;
2004 
2005     for ( i=94; i>=index ; --i ) {
2006 	if ( (*hm)[i>>3]&(0x80>>(i&7)) )
2007 	    (*hm)[(i+1)>>3] |= (0x80>>((i+1)&7));
2008 	else
2009 	    (*hm)[(i+1)>>3] &= ~(0x80>>((i+1)&7));
2010     }
2011     (*hm)[index>>3] &= ~(0x80>>(index&7));
2012 }
2013 
SCModifyHintMasksAdd(SplineChar * sc,int layer,StemInfo * new)2014 void SCModifyHintMasksAdd(SplineChar *sc,int layer, StemInfo *new) {
2015     SplineSet *spl;
2016     SplinePoint *sp;
2017     RefChar *ref;
2018     int index;
2019     StemInfo *h;
2020     int i;
2021 
2022     if ( layer<0 || layer>=sc->layer_cnt )
2023         return;
2024 
2025     /* We've added a new stem. Figure out where it goes and modify the */
2026     /*  hintmasks accordingly */
2027 
2028     for ( index=0, h=sc->hstem; h!=NULL && h!=new; ++index, h=h->next );
2029     if ( h==NULL )
2030 	for ( h=sc->vstem; h!=NULL && h!=new; ++index, h=h->next );
2031     if ( h==NULL )
2032 return;
2033 
2034     for ( i=0; i<sc->countermask_cnt; ++i )
2035 	ModifyHintMaskAdd(&sc->countermasks[i],index);
2036 
2037     for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
2038 	for ( sp = spl->first ; ; ) {
2039 	    ModifyHintMaskAdd(sp->hintmask,index);
2040 	    if ( sp->next==NULL )
2041 	break;
2042 	    sp = sp->next->to;
2043 	    if ( sp==spl->first )
2044 	break;
2045 	}
2046     }
2047 
2048     for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
2049 	for ( spl = ref->layers[0].splines; spl!=NULL; spl=spl->next ) {
2050 	    for ( sp = spl->first ; ; ) {
2051 		ModifyHintMaskAdd(sp->hintmask,index);
2052 		if ( sp->next==NULL )
2053 	    break;
2054 		sp = sp->next->to;
2055 		if ( sp==spl->first )
2056 	    break;
2057 	    }
2058 	}
2059     }
2060 }
2061 
SCFigureSimpleCounterMasks(SplineChar * sc)2062 static void SCFigureSimpleCounterMasks(SplineChar *sc) {
2063     SplineChar *scs[MmMax];
2064     int hadh3, hadv3, i, vbase;
2065     HintMask mask;
2066     StemInfo *h;
2067 
2068     if ( sc->countermask_cnt!=0 )
2069 return;
2070 
2071     scs[0] = sc;
2072     hadh3 = CvtPsStem3(NULL,scs,1,true,false);
2073     hadv3 = CvtPsStem3(NULL,scs,1,false,false);
2074     if ( hadh3 || hadv3 ) {
2075 	memset(mask,0,sizeof(mask));
2076 	if ( hadh3 ) mask[0] = 0x80|0x40|0x20;
2077 	if ( hadv3 ) {
2078 	    for ( h=sc->hstem, vbase=0; h!=NULL; h=h->next, ++vbase );
2079 	    for ( i=0; i<3 ; ++i ) {
2080 		int j = i+vbase;
2081 		mask[j>>3] |= (0x80>>(j&7));
2082 	    }
2083 	}
2084 	sc->countermask_cnt = 1;
2085 	sc->countermasks = malloc(sizeof(HintMask));
2086 	memcpy(sc->countermasks[0],mask,sizeof(HintMask));
2087 return;
2088     }
2089 }
2090 
2091 /* find all the other stems (after main) which seem to form a counter group */
2092 /*  with main. That is their stems have a considerable overlap (in the other */
2093 /*  coordinate) with main */
stemmatches(StemInfo * main)2094 static int stemmatches(StemInfo *main) {
2095     StemInfo *last=main, *test;
2096     real mlen, olen;
2097     int cnt;
2098 
2099     cnt = 1;		/* for the main stem */
2100     main->tobeused = true;
2101     mlen = HIlen(main);
2102     for ( test=main->next; test!=NULL; test=test->next )
2103 	test->tobeused = false;
2104     for ( test=main->next; test!=NULL; test=test->next ) {
2105 	if ( test->used || last->start+last->width>test->start || test->hintnumber==-1 )
2106     continue;
2107 	olen = HIoverlap(main->where,test->where);
2108 	if ( olen>mlen/3 && olen>HIlen(test)/3 ) {
2109 	    test->tobeused = true;
2110 	    ++cnt;
2111 	}
2112     }
2113 return( cnt );
2114 }
2115 
FigureCounters(StemInfo * stems,HintMask mask)2116 static int FigureCounters(StemInfo *stems,HintMask mask ) {
2117     StemInfo *h, *first;
2118 
2119     while ( stems!=NULL ) {
2120 	for ( first=stems; first!=NULL && first->used; first = first->next );
2121 	if ( first==NULL )
2122     break;
2123 	if ( first->where==NULL || first->hintnumber==-1 || stemmatches(first)<=2 ) {
2124 	    first->used = true;
2125 	    stems = first->next;
2126     continue;
2127 	}
2128 	for ( h = first; h!=NULL; h = h->next ) {
2129 	    if ( h->tobeused ) {
2130 		mask[h->hintnumber>>3] |= (0x80>>(h->hintnumber&7));
2131 		h->used = true;
2132 	    }
2133 	}
2134 return( true );
2135     }
2136 return( false );
2137 }
2138 
2139 /* Only used for metafont routine */
SCFigureVerticalCounterMasks(SplineChar * sc)2140 void SCFigureVerticalCounterMasks(SplineChar *sc) {
2141     HintMask masks[30];
2142     StemInfo *h;
2143     unsigned mc=0, i;
2144 
2145     /* I'm not supporting counter hints for mm fonts */
2146 
2147     if ( sc==NULL )
2148 return;
2149 
2150     free(sc->countermasks);
2151     sc->countermask_cnt = 0;
2152     sc->countermasks = NULL;
2153 
2154     for ( h=sc->vstem; h!=NULL ; h=h->next )
2155 	h->used = false;
2156 
2157     mc = 0;
2158 
2159     while ( mc<sizeof(masks)/sizeof(masks[0]) ) {
2160 	memset(masks[mc],'\0',sizeof(HintMask));
2161 	if ( !FigureCounters(sc->vstem,masks[mc]))
2162     break;
2163 	++mc;
2164     }
2165     if ( mc!=0 ) {
2166 	sc->countermask_cnt = mc;
2167 	sc->countermasks = malloc(mc*sizeof(HintMask));
2168 	for ( i=0; i<mc ; ++i )
2169 	    memcpy(sc->countermasks[i],masks[i],sizeof(HintMask));
2170     }
2171 }
2172 
SCFigureCounterMasks(SplineChar * sc)2173 void SCFigureCounterMasks(SplineChar *sc) {
2174     HintMask masks[30];
2175     uint32 script;
2176     StemInfo *h;
2177     unsigned mc=0, i;
2178 
2179     /* I'm not supporting counter hints for mm fonts */
2180 
2181     if ( sc==NULL )
2182 return;
2183 
2184     free(sc->countermasks);
2185     sc->countermask_cnt = 0;
2186     sc->countermasks = NULL;
2187 
2188     /* Check for h/vstem3 case */
2189     /* Which is allowed even for non-CJK letters */
2190     script = SCScriptFromUnicode(sc);
2191     if ( script==CHR('l','a','t','n') || script==CHR('c','y','r','l') ||
2192 	    script==CHR('g','r','e','k') ) {
2193 	SCFigureSimpleCounterMasks(sc);
2194 return;
2195     }
2196 
2197     for ( h=sc->hstem, i=0; h!=NULL ; h=h->next, ++i ) {
2198 	h->used = false;
2199 	h->hintnumber = i;
2200     }
2201     for ( h=sc->vstem; h!=NULL ; h=h->next, ++i  ) {
2202 	h->used = false;
2203 	h->hintnumber = i;
2204     }
2205 
2206     mc = 0;
2207 
2208     while ( mc<sizeof(masks)/sizeof(masks[0]) ) {
2209 	memset(masks[mc],'\0',sizeof(HintMask));
2210 	if ( !FigureCounters(sc->hstem,masks[mc]) && !FigureCounters(sc->vstem,masks[mc]))
2211     break;
2212 	++mc;
2213     }
2214     if ( mc!=0 ) {
2215 	sc->countermask_cnt = mc;
2216 	sc->countermasks = malloc(mc*sizeof(HintMask));
2217 	for ( i=0; i<mc ; ++i )
2218 	    memcpy(sc->countermasks[i],masks[i],sizeof(HintMask));
2219     }
2220 }
2221 
SCClearHintMasks(SplineChar * sc,int layer,int counterstoo)2222 void SCClearHintMasks(SplineChar *sc,int layer,int counterstoo) {
2223     MMSet *mm = sc->parent->mm;
2224     int i;
2225 
2226     if ( mm==NULL )
2227 	_SCClearHintMasks(sc,layer,counterstoo);
2228     else {
2229 	for ( i=0; i<mm->instance_count; ++i ) {
2230 	    if ( sc->orig_pos<mm->instances[i]->glyphcnt )
2231 		_SCClearHintMasks(mm->instances[i]->glyphs[sc->orig_pos],layer,counterstoo);
2232 	}
2233 	if ( sc->orig_pos<mm->normal->glyphcnt )
2234 	    _SCClearHintMasks(mm->normal->glyphs[sc->orig_pos],layer,counterstoo);
2235     }
2236 }
2237 
OnHHint(SplinePoint * sp,StemInfo * s)2238 static StemInfo *OnHHint(SplinePoint *sp, StemInfo *s) {
2239     StemInfo *possible=NULL;
2240     HintInstance *hi;
2241 
2242     if ( sp==NULL )
2243 return( NULL );
2244 
2245     for ( ; s!=NULL; s=s->next ) {
2246 	if ( sp->me.y<s->start )
2247 return( possible );
2248 	if ( s->start==sp->me.y || s->start+s->width==sp->me.y ) {
2249 	    if ( !s->hasconflicts )
2250 return( s );
2251 	    for ( hi=s->where; hi!=NULL; hi=hi->next ) {
2252 		if ( hi->begin<=sp->me.x && hi->end>=sp->me.x )
2253 return( s );
2254 	    }
2255 	    if ( !s->used )
2256 		possible = s;
2257 	}
2258     }
2259 return( possible );
2260 }
2261 
OnVHint(SplinePoint * sp,StemInfo * s)2262 static StemInfo *OnVHint(SplinePoint *sp, StemInfo *s) {
2263     StemInfo *possible=NULL;
2264     HintInstance *hi;
2265 
2266     if ( sp==NULL )
2267 return( NULL );
2268 
2269     for ( ; s!=NULL; s=s->next ) {
2270 	if ( sp->me.x<s->start )
2271 return( possible );
2272 	if ( s->start==sp->me.x || s->start+s->width==sp->me.x ) {
2273 	    if ( !s->hasconflicts )
2274 return( s );
2275 	    for ( hi=s->where; hi!=NULL; hi=hi->next ) {
2276 		if ( hi->begin<=sp->me.y && hi->end>=sp->me.y )
2277 return( s );
2278 	    }
2279 	    if ( !s->used )
2280 		possible = s;
2281 	}
2282     }
2283 return( possible );
2284 }
2285 
2286 /* Does h have a conflict with any of the stems in the list which have bits */
2287 /*  set in the mask */
ConflictsWithMask(StemInfo * stems,HintMask mask,StemInfo * h)2288 static int ConflictsWithMask(StemInfo *stems, HintMask mask,StemInfo *h) {
2289     while ( stems!=NULL && stems->start<=h->start+h->width ) {
2290 	if ( stems->start+stems->width>=h->start && stems!=h ) {
2291 	    if ( stems->hintnumber!=-1 &&
2292 		    (mask[stems->hintnumber>>3]&(0x80>>(stems->hintnumber&7))) )
2293 return( true );
2294 	}
2295 	stems = stems->next;
2296     }
2297 return( false );
2298 }
2299 
2300 /* All instances of a MM set must have the same hint mask at all points */
FigureHintMask(SplineChar * scs[MmMax],SplinePoint * to[MmMax],int instance_count,HintMask mask)2301 static void FigureHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count,
2302 	HintMask mask) {
2303     StemInfo *s;
2304     int i;
2305     SplinePoint *sp;
2306 
2307     memset(mask,'\0',sizeof(HintMask));
2308 
2309     /* Install all hints that are always active */
2310     i=0; {
2311 	SplineChar *sc = scs[i];
2312 
2313 	if ( sc==NULL )
2314 return;
2315 
2316 	for ( s=sc->hstem; s!=NULL; s=s->next )
2317 	    if ( s->hintnumber!=-1 && !s->hasconflicts )
2318 		mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
2319 	for ( s=sc->vstem; s!=NULL; s=s->next )
2320 	    if ( s->hintnumber!=-1 && !s->hasconflicts )
2321 		mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
2322 
2323 	if ( sc->hconflicts ) {
2324 	    for ( sp=to[i]; sp!=NULL; ) {
2325 		s = OnHHint(sp,sc->hstem);
2326 		if ( s!=NULL && s->hintnumber!=-1 ) {
2327 		    if ( ConflictsWithMask(scs[i]->hstem,mask,s))
2328 	    break;
2329 		    mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
2330 		}
2331 		if ( sp->next==NULL )
2332 	    break;
2333 		sp = sp->next->to;
2334 		if ( to[i]==sp )
2335 	    break;
2336 	    }
2337 	}
2338 	if ( sc->vconflicts ) {
2339 	    for ( sp=to[i]; sp!=NULL; ) {
2340 		s = OnVHint(sp,sc->vstem);
2341 		if ( s!=NULL && s->hintnumber!=-1 ) {
2342 		    if ( ConflictsWithMask(scs[i]->vstem,mask,s))
2343 	    break;
2344 		    mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
2345 		}
2346 		if ( sp->next==NULL )
2347 	    break;
2348 		sp = sp->next->to;
2349 		if ( to[i]==sp )
2350 	    break;
2351 	    }
2352 	}
2353     }
2354     for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
2355 	chunkfree(to[i]->hintmask,sizeof(HintMask));
2356 	to[i]->hintmask = chunkalloc(sizeof(HintMask));
2357 	memcpy(to[i]->hintmask,mask,sizeof(HintMask));
2358     }
2359 }
2360 
TestHintMask(SplineChar * scs[MmMax],SplinePoint * to[MmMax],int instance_count,HintMask mask)2361 static int TestHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count,
2362 	HintMask mask) {
2363     StemInfo *h=NULL, *v=NULL;
2364     int i;
2365 
2366     for ( i=0; i<instance_count; ++i ) {
2367 	SplineChar *sc = scs[i];
2368 
2369 	if ( sc==NULL || (!sc->hconflicts && !sc->vconflicts ))
2370     continue;
2371 
2372 	/* Does this point lie on any hints? */
2373 	if ( scs[i]->hconflicts )
2374 	    h = OnHHint(to[i],sc->hstem);
2375 	if ( scs[i]->vconflicts )
2376 	    v = OnVHint(to[i],sc->vstem);
2377 
2378 	/* Need to set this hint */
2379 	if ( (h!=NULL && h->hintnumber!=-1 && (mask[h->hintnumber>>3]&(0x80>>(h->hintnumber&7)))==0 ) ||
2380 	     (v!=NULL && v->hintnumber!=-1 && (mask[v->hintnumber>>3]&(0x80>>(v->hintnumber&7)))==0 ))
2381     break;
2382     }
2383     if ( i==instance_count )	/* All hint masks were ok */
2384 return( false );
2385 
2386     FigureHintMask(scs,to,instance_count,mask);
2387 return( true );
2388 }
2389 
UnnumberHints(SplineChar * sc)2390 static void UnnumberHints(SplineChar *sc) {
2391     StemInfo *h;
2392 
2393     for ( h=sc->hstem; h!=NULL; h=h->next )
2394 	h->hintnumber = -1;
2395     for ( h=sc->vstem; h!=NULL; h=h->next )
2396 	h->hintnumber = -1;
2397 }
2398 
NumberHints(SplineChar * sc)2399 static int NumberHints(SplineChar *sc) {
2400     StemInfo *h;
2401     int hcnt=0;
2402 
2403     for ( h=sc->hstem; h!=NULL; h=h->next )
2404 	h->hintnumber = hcnt>=HntMax ? -1 : hcnt++;
2405     for ( h=sc->vstem; h!=NULL; h=h->next )
2406 	h->hintnumber = hcnt>=HntMax ? -1 : hcnt++;
2407 return( hcnt );
2408 }
2409 
UntickHints(SplineChar * sc)2410 static void UntickHints(SplineChar *sc) {
2411     StemInfo *h;
2412 
2413     for ( h=sc->hstem; h!=NULL; h=h->next )
2414 	h->used = false;
2415     for ( h=sc->vstem; h!=NULL; h=h->next )
2416 	h->used = false;
2417 }
2418 
2419 struct coords {
2420     real coords[MmMax];
2421     struct coords *next;
2422 };
2423 
2424 typedef struct mmh {
2425     StemInfo *hints[MmMax], *map[MmMax];
2426     struct coords *where;
2427     struct mmh *next;
2428 } MMH;
2429 
AddCoord(MMH * mmh,SplinePoint * sps[MmMax],int instance_count,int ish)2430 static void AddCoord(MMH *mmh,SplinePoint *sps[MmMax],int instance_count, int ish) {
2431     struct coords *coords;
2432     int i;
2433 
2434     coords = chunkalloc(sizeof(struct coords));
2435     coords->next = mmh->where;
2436     mmh->where = coords;
2437     if ( ish )
2438 	for ( i=0; i<instance_count; ++i )
2439 	    coords->coords[i] = sps[i]->me.x;
2440     else
2441 	for ( i=0; i<instance_count; ++i )
2442 	    coords->coords[i] = sps[i]->me.y;
2443 }
2444 
AddHintSet(MMH * hints,StemInfo * h[MmMax],int instance_count,SplinePoint * sps[MmMax],int ish)2445 static MMH *AddHintSet(MMH *hints,StemInfo *h[MmMax], int instance_count,
2446 	SplinePoint *sps[MmMax], int ish) {
2447     int i, cnt, bestc;
2448     MMH *test, *best;
2449 
2450     for ( i=0; i<instance_count; ++i )
2451 	if ( h[i]==NULL )
2452 return( hints );
2453 
2454     best = NULL; bestc = 0;
2455     for ( test=hints; test!=NULL; test=test->next ) {
2456 	cnt = 0;
2457 	for ( i=0; i<instance_count; ++i )
2458 	    if ( test->hints[i]==h[i] )
2459 		++cnt;
2460 	if ( cnt==instance_count ) {
2461 	    AddCoord(test,sps,instance_count,ish);
2462 return( hints );
2463 	}
2464 	if ( cnt>bestc ) {
2465 	    bestc = cnt;
2466 	    best = test;
2467 	}
2468     }
2469     test = chunkalloc(sizeof(MMH));
2470     test->next = hints;
2471     AddCoord(test,sps,instance_count,ish);
2472     for ( i=0; i<instance_count; ++i )
2473 	test->hints[i]=h[i];
2474     if ( bestc!=0 ) {
2475 	for ( i=0; i<instance_count; ++i ) {
2476 	    if ( best->hints[i]==h[i] ) {
2477 		h[i]->hasconflicts = true;
2478 		test->map[i] = chunkalloc(sizeof(StemInfo));
2479 		*test->map[i] = *h[i];
2480 		test->map[i]->where = NULL;
2481 		test->map[i]->used = true;
2482 		h[i]->next = test->map[i];
2483 	    } else
2484 		test->map[i] = h[i];
2485 	}
2486     } else {
2487 	for ( i=0; i<instance_count; ++i )
2488 	    test->map[i]=h[i];
2489     }
2490 return( test );
2491 }
2492 
CompareMMH(MMH * mmh1,MMH * mmh2,int instance_count)2493 static int CompareMMH(MMH *mmh1,MMH *mmh2, int instance_count) {
2494     int i;
2495 
2496     if ( mmh1->map[0]==NULL )
2497 return( 1 );
2498     if ( mmh2->map[0]==NULL )
2499 return( -1 );
2500 
2501     for ( i=0; i<instance_count; ++i ) {
2502 	if ( mmh1->map[i]->start!=mmh2->map[i]->start ) {
2503 	    if ( mmh1->map[i]->start > mmh2->map[i]->start )
2504 return( 1 );
2505 	    else
2506 return( -1 );
2507 	}
2508     }
2509     for ( i=0; i<instance_count; ++i ) {
2510 	if ( mmh1->map[i]->width!=mmh2->map[i]->width ) {
2511 	    if ( mmh1->map[i]->width > mmh2->map[i]->width )
2512 return( 1 );
2513 	    else
2514 return( -1 );
2515 	}
2516     }
2517 return( 0 );
2518 }
2519 
SortMMH(MMH * head,int instance_count)2520 static MMH *SortMMH(MMH *head,int instance_count) {
2521     MMH *mmh, *p, *smallest, *psmallest, *test, *ptest;
2522 
2523     for ( mmh = head, p=NULL; mmh!=NULL ; ) {
2524 	smallest = mmh; psmallest = p;
2525 	ptest = mmh; test = mmh->next;
2526 	while ( test!=NULL ) {
2527 	    if ( CompareMMH(test,smallest,instance_count)<0 ) {
2528 		smallest = test;
2529 		psmallest = ptest;
2530 	    }
2531 	    ptest = test;
2532 	    test = test->next;
2533 	}
2534 	if ( smallest!=mmh ) {
2535 	    if ( p==NULL )
2536 		head = smallest;
2537 	    else
2538 		p->next = smallest;
2539 	    if ( mmh->next==smallest ) {
2540 		mmh->next = smallest->next;
2541 		smallest->next = mmh;
2542 	    } else {
2543 		test = mmh->next;
2544 		mmh->next = smallest->next;
2545 		smallest->next = test;
2546 		psmallest->next = mmh;
2547 	    }
2548 	}
2549 	p = smallest;
2550 	mmh = smallest->next;
2551     }
2552 return( head );
2553 }
2554 
NumberMMH(MMH * mmh,int hstart,int instance_count)2555 static int NumberMMH(MMH *mmh,int hstart,int instance_count) {
2556     int i;
2557     HintInstance *hi, *n;
2558     struct coords *coords;
2559 
2560     while ( mmh!=NULL ) {
2561 	for ( i=0; i<instance_count; ++i ) {
2562 	    StemInfo *h = mmh->map[i];
2563 	    if ( h==NULL )
2564 	continue;
2565 
2566 	    h->hintnumber = hstart;
2567 
2568 	    for ( hi=h->where; hi!=NULL; hi=n ) {
2569 		n = hi->next;
2570 		chunkfree(hi,sizeof(HintInstance));
2571 	    }
2572 	    h->where = NULL;
2573 	    for ( coords=mmh->where; coords!=NULL; coords = coords->next ) {
2574 		hi = chunkalloc(sizeof(HintInstance));
2575 		hi->next = h->where;
2576 		h->where = hi;
2577 		hi->begin = coords->coords[i]-1;
2578 		hi->end = coords->coords[i]+1;
2579 	    }
2580 	}
2581 	if ( mmh->map[0]!=NULL ) ++hstart;
2582 	mmh = mmh->next;
2583     }
2584 return( hstart );
2585 }
2586 
SortMMH2(SplineChar * scs[MmMax],MMH * mmh,int instance_count,int ish)2587 static void SortMMH2(SplineChar *scs[MmMax],MMH *mmh,int instance_count,int ish) {
2588     int i;
2589     StemInfo *h, *n;
2590     MMH *m;
2591 
2592     for ( i=0; i<instance_count; ++i ) {
2593 	for ( h= ish ? scs[i]->hstem : scs[i]->vstem; h!=NULL; h=n ) {
2594 	    n = h->next;
2595 	    if ( h->hintnumber==-1 )
2596 		StemInfoFree(h);
2597 	}
2598 	n = NULL;
2599 	for ( m = mmh ; m!=NULL; m=m->next ) {
2600 	    h = m->map[i];
2601 	    if ( n!=NULL )
2602 		n->next = h;
2603 	    else if ( ish )
2604 		scs[i]->hstem = h;
2605 	    else
2606 		scs[i]->vstem = h;
2607 	    n = h;
2608 	}
2609 	if ( n!=NULL )
2610 	    n->next = NULL;
2611 	else if ( ish )
2612 	    scs[i]->hstem = NULL;
2613 	else
2614 	    scs[i]->vstem = NULL;
2615     }
2616 }
2617 
MMHFreeList(MMH * mmh)2618 static void MMHFreeList(MMH *mmh) {
2619     MMH *mn;
2620     struct coords *c, *n;
2621 
2622     for ( ; mmh!=NULL; mmh = mn ) {
2623 	mn = mmh->next;
2624 	for ( c=mmh->where; c!=NULL; c=n ) {
2625 	    n = c->next;
2626 	    chunkfree(c,sizeof(struct coords));
2627 	}
2628 	chunkfree(mmh,sizeof(struct coords));
2629     }
2630 }
2631 
SplResolveSplitHints(SplineChar * scs[MmMax],SplineSet * spl[MmMax],int instance_count,MMH ** hs,MMH ** vs)2632 static void SplResolveSplitHints(SplineChar *scs[MmMax], SplineSet *spl[MmMax],
2633 	int instance_count, MMH **hs, MMH **vs) {
2634     SplinePoint *to[MmMax];
2635     StemInfo *h[MmMax], *v[MmMax];
2636     int i, anymore;
2637 
2638     for (;;) {
2639 	for ( i=0; i<instance_count; ++i ) {
2640 	    if ( spl[i]!=NULL )
2641 		to[i] = spl[i]->first;
2642 	    else
2643 		to[i] = NULL;
2644 	}
2645 	for (;;) {
2646 	    for ( i=0; i<instance_count; ++i ) {
2647 		h[i] = OnHHint(to[i],scs[i]->hstem);
2648 		v[i] = OnVHint(to[i],scs[i]->vstem);
2649 	    }
2650 	    *hs = AddHintSet(*hs,h,instance_count,to,true);
2651 	    *vs = AddHintSet(*vs,v,instance_count,to,false);
2652 	    anymore = false;
2653 	    for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
2654 		if ( to[i]->next==NULL ) to[i] = NULL;
2655 		else {
2656 		    to[i] = to[i]->next->to;
2657 		    if ( to[i]==spl[i]->first ) to[i] = NULL;
2658 		}
2659 		if ( to[i]!=NULL ) anymore = true;
2660 	    }
2661 	    if ( !anymore )
2662 	break;
2663 	}
2664 	anymore = false;
2665 	for ( i=0; i<instance_count; ++i ) {
2666 	    if ( spl[i]!=NULL )
2667 		spl[i] = spl[i]->next;
2668 	    if ( spl[i]!=NULL ) anymore = true;
2669 	}
2670 	if ( !anymore )
2671     break;
2672     }
2673 }
2674 
ResolveSplitHints(SplineChar * scs[16],int layer,int instance_count)2675 static void ResolveSplitHints(SplineChar *scs[16],int layer,int instance_count) {
2676     /* It is possible for a single hint in one mm instance to split into two */
2677     /*  in a different MM set. For example, we have two stems which happen */
2678     /*  to line up in one instance but which do not in another instance. */
2679     /* It is even possible that there could be no instance with any conflicts */
2680     /*  but some of the intermediate forms might conflict. */
2681     /* We can't deal (nor can postscript) with the case where hints change order*/
2682     SplinePointList *spl[MmMax];
2683     RefChar *ref[MmMax];
2684     int i, hcnt, hmax=0, anymore;
2685     MMH *hs=NULL, *vs=NULL;
2686 
2687     for ( i=0; i<instance_count; ++i ) {
2688 	hcnt = NumberHints(scs[i]);
2689 	UntickHints(scs[i]);
2690 	if ( hcnt>hmax ) hmax = hcnt;
2691 	spl[i] = scs[i]->layers[layer].splines;
2692     }
2693     if ( hmax==0 )
2694 return;
2695 
2696     SplResolveSplitHints(scs,spl,instance_count,&hs,&vs);
2697     anymore = false;
2698     for ( i=0; i<instance_count; ++i ) {
2699 	ref[i] = scs[i]->layers[layer].refs;
2700 	if ( ref[i]!=NULL ) anymore = true;
2701     }
2702     while ( anymore ) {
2703 	for ( i=0; i<instance_count; ++i )
2704 	    spl[i] = ( ref[i]!=NULL ) ? ref[i]->layers[0].splines : NULL;
2705 	SplResolveSplitHints(scs,spl,instance_count,&hs,&vs);
2706 	anymore = false;
2707 	for ( i=0; i<instance_count; ++i ) {
2708 	    if ( ref[i]!=NULL ) {
2709 		ref[i] = ref[i]->next;
2710 		if ( ref[i]!=NULL ) anymore = true;
2711 	    }
2712 	}
2713     }
2714 
2715     for ( i=0; i<instance_count; ++i )
2716 	UnnumberHints(scs[i]);
2717     hs = SortMMH(hs,instance_count);
2718     vs = SortMMH(vs,instance_count);
2719     hcnt = NumberMMH(hs,0,instance_count);
2720     hcnt = NumberMMH(vs,hcnt,instance_count);
2721     SortMMH2(scs,hs,instance_count,true);
2722     SortMMH2(scs,vs,instance_count,false);
2723     MMHFreeList(hs);
2724     MMHFreeList(vs);
2725 }
2726 
SplFigureHintMasks(SplineChar * scs[MmMax],SplineSet * spl[MmMax],int instance_count,HintMask mask,int inited)2727 static int SplFigureHintMasks(SplineChar *scs[MmMax], SplineSet *spl[MmMax],
2728 	int instance_count, HintMask mask, int inited) {
2729     SplinePoint *to[MmMax];
2730     int i, anymore;
2731 
2732     anymore = false;
2733     for ( i=0; i<instance_count; ++i ) {
2734 	if ( spl[i]!=NULL ) {
2735 	    SplineSetReverse(spl[i]);
2736 	    to[i] = spl[i]->first;
2737 	    anymore = true;
2738 	} else
2739 	    to[i] = NULL;
2740     }
2741 
2742     /* Assign the initial hint mask */
2743     if ( anymore && !inited ) {
2744 	FigureHintMask(scs,to,instance_count,mask);
2745 	inited = true;
2746     }
2747 
2748     for (;;) {
2749 	for ( i=0; i<instance_count; ++i ) {
2750 	    if ( spl[i]!=NULL )
2751 		to[i] = spl[i]->first;
2752 	    else
2753 		to[i] = NULL;
2754 	}
2755 	for (;;) {
2756 	    TestHintMask(scs,to,instance_count,mask);
2757 	    anymore = false;
2758 	    for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
2759 		if ( to[i]->next==NULL ) to[i] = NULL;
2760 		else {
2761 		    to[i] = to[i]->next->to;
2762 		    if ( to[i]==spl[i]->first ) to[i] = NULL;
2763 		}
2764 		if ( to[i]!=NULL ) anymore = true;
2765 	    }
2766 	    if ( !anymore )
2767 	break;
2768 	}
2769 	anymore = false;
2770 	for ( i=0; i<instance_count; ++i ) {
2771 	    if ( spl[i]!=NULL ) {
2772 		SplineSetReverse(spl[i]);
2773 		spl[i] = spl[i]->next;
2774 	    }
2775 	    if ( spl[i]!=NULL ) {
2776 		anymore = true;
2777 		SplineSetReverse(spl[i]);
2778 	    }
2779 	}
2780 	if ( !anymore )
2781     break;
2782     }
2783 return( inited );
2784 }
2785 
SCFigureHintMasks(SplineChar * sc,int layer)2786 void SCFigureHintMasks(SplineChar *sc,int layer) {
2787     SplineChar *scs[MmMax];
2788     SplinePointList *spl[MmMax];
2789     RefChar *ref[MmMax];
2790     MMSet *mm = sc->parent->mm;
2791     int i, instance_count, conflicts, anymore, inited;
2792     HintMask mask;
2793 
2794     if ( mm==NULL ) {
2795 	scs[0] = sc;
2796 	instance_count = 1;
2797 	SCClearHintMasks(sc,layer,false);
2798     } else {
2799 	if ( mm->apple )
2800 return;
2801 	instance_count = mm->instance_count;
2802 	for ( i=0; i<instance_count; ++i )
2803 	    if ( sc->orig_pos < mm->instances[i]->glyphcnt ) {
2804 		scs[i] = mm->instances[i]->glyphs[sc->orig_pos];
2805 		SCClearHintMasks(scs[i],layer,false);
2806 	    }
2807 	ResolveSplitHints(scs,layer,instance_count);
2808     }
2809     conflicts = false;
2810     for ( i=0; i<instance_count; ++i ) {
2811 	NumberHints(scs[i]);
2812 	if ( scs[i]->hconflicts || scs[i]->vconflicts )
2813 	    conflicts = true;
2814     }
2815     if ( !conflicts && instance_count==1 ) {	/* All hints always active */
2816 	SCFigureSimpleCounterMasks(sc);
2817 return;						/* In an MM font we may still need to resolve things like different numbers of hints */
2818     }
2819 
2820     for ( i=0; i<instance_count; ++i ) {
2821 	spl[i] = scs[i]->layers[layer].splines;
2822 	ref[i] = scs[i]->layers[layer].refs;
2823     }
2824     inited = SplFigureHintMasks(scs,spl,instance_count,mask,false);
2825     for (;;) {
2826 	for ( i=0; i<instance_count; ++i ) {
2827 	    if ( ref[i]!=NULL )
2828 		spl[i] = ref[i]->layers[0].splines;
2829 	}
2830 	inited = SplFigureHintMasks(scs,spl,instance_count,mask,inited);
2831 	anymore = false;
2832 	for ( i=0; i<instance_count; ++i ) {
2833 	    if ( ref[i]!=NULL ) {
2834 		ref[i] = ref[i]->next;
2835 		if ( ref[i]!=NULL ) anymore = true;
2836 	    }
2837 	}
2838 	if ( !anymore )
2839     break;
2840     }
2841     if ( instance_count==1 )
2842 	SCFigureSimpleCounterMasks(sc);
2843 }
2844 
GDFindStems(struct glyphdata * gd,int major)2845 static StemInfo *GDFindStems(struct glyphdata *gd, int major) {
2846     int i;
2847     StemInfo *head = NULL, *cur, *p, *t;
2848     StemBundle *bundle = major ? gd->vbundle : gd->hbundle;
2849     StemData *stem;
2850     int other = !major;
2851     double l, r;
2852 
2853     for ( i=0; i<bundle->cnt; ++i ) {
2854 	stem = bundle->stemlist[i];
2855 	l = (&stem->left.x)[other];
2856         r = (&stem->right.x)[other];
2857 	cur = chunkalloc( sizeof( StemInfo ));
2858 	if ( l<r ) {
2859 	    cur->start = l;
2860 	    cur->width = r - l;
2861 	    cur->haspointleft = stem->lpcnt > 0;
2862 	    cur->haspointright = stem->rpcnt > 0;
2863 	} else {
2864 	    cur->start = r;
2865 	    cur->width = l - r;
2866 	    cur->haspointleft = stem->rpcnt > 0;
2867 	    cur->haspointright = stem->lpcnt > 0;
2868 	}
2869         cur->ghost = stem->ghost;
2870 	for ( p=NULL, t=head; t!=NULL ; p=t, t=t->next ) {
2871 	    if ( cur->start<=t->start )
2872 	break;
2873 	}
2874 	cur->next = t;
2875 	if ( p==NULL )
2876 	    head = cur;
2877 	else
2878 	    p->next = cur;
2879 	cur->where = StemAddHIFromActive(stem,major);
2880     }
2881     head = StemRemoveFlexCandidates(head);
2882 return( head );
2883 }
2884 
GDFindDStems(struct glyphdata * gd)2885 static DStemInfo *GDFindDStems(struct glyphdata *gd) {
2886     int i;
2887     DStemInfo *head = NULL, *cur ;
2888     struct stemdata *stem;
2889 
2890     for ( i=0; i<gd->stemcnt; ++i ) {
2891 	stem = &gd->stems[i];
2892         /* A real diagonal stem should consist of one or more continuous
2893          * ranges. Thus the number of active zones should be less then the
2894          * number of stem chunks (i. e. pairs of the opposite points). If
2895          * each chunk has its own active zone, then we probably have got
2896          * not a real stem, but rather two (or more) separate point pairs,
2897          * which occasionally happened to have nearly the same vectors and
2898          * to be positioned on the same lines */
2899 	if ( stem->toobig )
2900     continue;
2901 
2902         if (( stem->unit.y > -.05 && stem->unit.y < .05 ) ||
2903             ( stem->unit.x > -.05 && stem->unit.x < .05 ))
2904     continue;
2905 
2906 	if ( stem->lpcnt < 2 || stem->rpcnt < 2 )
2907     continue;
2908         cur = chunkalloc( sizeof(DStemInfo) );
2909         cur->left = stem->left;
2910         cur->right = stem->right;
2911         cur->unit = stem->unit;
2912         cur->where = DStemAddHIFromActive( stem );
2913         MergeDStemInfo(gd->sf, &head, cur);
2914     }
2915 return( head );
2916 }
2917 
2918 
inorder(real a,real b,real c)2919 static bool inorder( real a, real b, real c )
2920 {
2921     return a < b && b < c;
2922 }
2923 
2924 /**
2925  * If fluffy is near enough to exact then clamp to exact.
2926  * If fluffy is more than Tolerance away from exact then
2927  * just return fluffy (no change).
2928  */
clampToIfNear(real exact,real fluffy,real Tolerance)2929 static real clampToIfNear( real exact, real fluffy, real Tolerance )
2930 {
2931     if( inorder( exact - Tolerance, fluffy, exact + Tolerance ))
2932 	return exact;
2933 
2934     return fluffy;
2935 }
2936 
2937 
_SplineCharAutoHint(SplineChar * sc,int layer,BlueData * bd,struct glyphdata * gd2,int gen_undoes)2938 void _SplineCharAutoHint( SplineChar *sc, int layer, BlueData *bd, struct glyphdata *gd2,
2939 	int gen_undoes ) {
2940     struct glyphdata *gd;
2941 
2942     if ( gen_undoes )
2943 	SCPreserveHints(sc,layer);
2944     StemInfosFree(sc->vstem); sc->vstem=NULL;
2945     StemInfosFree(sc->hstem); sc->hstem=NULL;
2946     DStemInfosFree(sc->dstem); sc->dstem=NULL;
2947     MinimumDistancesFree(sc->md); sc->md=NULL;
2948 
2949     free(sc->countermasks);
2950     sc->countermasks = NULL; sc->countermask_cnt = 0;
2951     /* We'll free the hintmasks when we call SCFigureHintMasks */
2952 
2953     sc->changedsincelasthinted = false;
2954     sc->manualhints = false;
2955 
2956     if ( (gd=gd2)==NULL )
2957 	gd = GlyphDataBuild( sc,layer,bd,false );
2958     if ( gd!=NULL ) {
2959 
2960 	sc->vstem = GDFindStems(gd,1);
2961 	sc->hstem = GDFindStems(gd,0);
2962 
2963 	if ( !gd->only_hv )
2964 	    sc->dstem = GDFindDStems(gd);
2965 	if ( gd2==NULL ) GlyphDataFree(gd);
2966     }
2967 
2968     real AutohintRoundingTolerance = 0.005;
2969     StemInfo* s = sc->hstem;
2970     for( ; s; s = s->next )
2971     {
2972 	s->width = clampToIfNear( 20.0, s->width, AutohintRoundingTolerance );
2973 	s->width = clampToIfNear( 21.0, s->width, AutohintRoundingTolerance );
2974     }
2975 
2976     AutoHintRefs(sc,layer,bd,false,gen_undoes);
2977 }
2978 
__SplineCharAutoHint(SplineChar * sc,int layer,BlueData * bd,int gen_undoes)2979 static void __SplineCharAutoHint( SplineChar *sc, int layer, BlueData *bd, int gen_undoes ) {
2980     MMSet *mm = sc->parent->mm;
2981     int i;
2982 
2983     if ( mm==NULL )
2984 	_SplineCharAutoHint(sc,layer,bd,NULL,gen_undoes);
2985     else {
2986 	for ( i=0; i<mm->instance_count; ++i )
2987 	    if ( sc->orig_pos < mm->instances[i]->glyphcnt )
2988 		_SplineCharAutoHint(mm->instances[i]->glyphs[sc->orig_pos],layer,NULL,NULL,gen_undoes);
2989 	if ( sc->orig_pos < mm->normal->glyphcnt )
2990 	    _SplineCharAutoHint(mm->normal->glyphs[sc->orig_pos],layer,NULL,NULL,gen_undoes);
2991     }
2992     SCFigureHintMasks(sc,layer);
2993     SCUpdateAll(sc);
2994 }
2995 
SplineCharAutoHint(SplineChar * sc,int layer,BlueData * bd)2996 void SplineCharAutoHint( SplineChar *sc, int layer, BlueData *bd ) {
2997     __SplineCharAutoHint(sc,layer,bd,true);
2998 }
2999 
SFSCAutoHint(SplineChar * sc,int layer,BlueData * bd)3000 void SFSCAutoHint( SplineChar *sc, int layer, BlueData *bd ) {
3001     RefChar *ref;
3002 
3003     if ( sc->ticked )
3004 return;
3005     for ( ref=sc->layers[ly_fore].refs; ref!=NULL; ref=ref->next ) {
3006 	if ( !ref->sc->ticked )
3007 	    SFSCAutoHint(ref->sc,layer,bd);
3008     }
3009     sc->ticked = true;
3010     SplineCharAutoHint(sc,layer,bd);
3011 }
3012 
SFNeedsAutoHint(SplineFont * _sf)3013 int SFNeedsAutoHint( SplineFont *_sf) {
3014     int i,k;
3015     SplineFont *sf;
3016 
3017     k=0;
3018     do {
3019 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
3020 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3021 	    if ( sf->glyphs[i]->changedsincelasthinted &&
3022 		    !sf->glyphs[i]->manualhints )
3023 return( true );
3024 	}
3025 	++k;
3026     } while ( k<_sf->subfontcnt );
3027 return( false );
3028 }
3029 
SplineFontAutoHint(SplineFont * _sf,int layer)3030 void SplineFontAutoHint( SplineFont *_sf,int layer) {
3031     int i,k;
3032     SplineFont *sf;
3033     BlueData *bd = NULL, _bd;
3034     SplineChar *sc;
3035 
3036     if ( _sf->mm==NULL ) {
3037 	QuickBlues(_sf,layer,&_bd);
3038 	bd = &_bd;
3039     }
3040 
3041     /* Tick the ones we don't want to AH, untick the ones that need AH */
3042     k=0;
3043     do {
3044 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
3045 	for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL )
3046 	    sc->ticked = ( !sc->changedsincelasthinted || sc->manualhints );
3047 	++k;
3048     } while ( k<_sf->subfontcnt );
3049 
3050     k=0;
3051     do {
3052 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
3053 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3054 	    if ( sf->glyphs[i]->changedsincelasthinted &&
3055 		    !sf->glyphs[i]->manualhints )
3056 		SFSCAutoHint(sf->glyphs[i],layer,bd);
3057 	    if ( !ff_progress_next()) {
3058 		k = _sf->subfontcnt+1;
3059 	break;
3060 	    }
3061 	}
3062 	++k;
3063     } while ( k<_sf->subfontcnt );
3064 }
3065 
SplineFontAutoHintRefs(SplineFont * _sf,int layer)3066 void SplineFontAutoHintRefs( SplineFont *_sf,int layer) {
3067     int i,k;
3068     SplineFont *sf;
3069     BlueData *bd = NULL, _bd;
3070     SplineChar *sc;
3071 
3072     if ( _sf->mm==NULL ) {
3073 	QuickBlues(_sf,layer,&_bd);
3074 	bd = &_bd;
3075     }
3076 
3077     k=0;
3078     do {
3079 	sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
3080 	for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc = sf->glyphs[i])!=NULL ) {
3081 	    if ( sc->changedsincelasthinted &&
3082 		    !sc->manualhints &&
3083 		    (sc->layers[layer].refs!=NULL && sc->layers[layer].splines==NULL)) {
3084 		SCPreserveHints(sc,layer);
3085 		StemInfosFree(sc->vstem); sc->vstem=NULL;
3086 		StemInfosFree(sc->hstem); sc->hstem=NULL;
3087 		AutoHintRefs(sc,layer,bd,true,true);
3088 	    }
3089 	}
3090 	++k;
3091     } while ( k<_sf->subfontcnt );
3092 }
3093 
FigureStems(SplineFont * sf,real snaps[12],real cnts[12],int which)3094 static void FigureStems( SplineFont *sf, real snaps[12], real cnts[12],
3095 	int which ) {
3096     int i, j, k, cnt, smax=0, smin=2000;
3097     real stemwidths[2000];
3098     StemInfo *stems, *test;
3099     int len;
3100     HintInstance *hi;
3101 
3102     memset(stemwidths,'\0',sizeof(stemwidths));
3103 
3104     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3105 	stems = which?sf->glyphs[i]->hstem:sf->glyphs[i]->vstem;
3106 	for ( test=stems; test!=NULL; test = test->next ) if ( !test->ghost ) {
3107 	    if ( (j=test->width)<0 ) j= -j;
3108 	    if ( j<2000 ) {
3109 		len = 0;
3110 		for ( hi=test->where; hi!=NULL; hi=hi->next )
3111 		    len += hi->end-hi->begin;
3112 		if ( len==0 ) len = 100;
3113 		stemwidths[j] += len;
3114 		if ( smax<j ) smax=j;
3115 		if ( smin>j ) smin=j;
3116 	    }
3117 	}
3118     }
3119 
3120     for ( i=smin, cnt=0; i<=smax; ++i ) {
3121 	if ( stemwidths[i]!=0 )
3122 	    ++cnt;
3123     }
3124 
3125     if ( cnt>12 ) {
3126 	/* Merge width windows */
3127 	int windsize=3, j;
3128 	for ( i=smin; i<=smax; ++i ) if ( stemwidths[i]!=0 ) {
3129 	    if ( (j = i-windsize)<0 ) j=0;
3130 	    for ( ; j<smax && j<=i+windsize; ++j )
3131 		if ( stemwidths[i]<stemwidths[j] )
3132 	    break;
3133 	    if ( j==smax || j>i+windsize ) {
3134 		if ( (j = i-windsize)<0 ) j=0;
3135 		for ( ; j<smax && j<=i+windsize; ++j ) if ( j!=i ) {
3136 		    stemwidths[i] += stemwidths[j];
3137 		    stemwidths[j] = 0;
3138 		}
3139 	    }
3140 	}
3141 	/* Merge adjacent widths */
3142 	for ( i=smin; i<=smax; ++i ) {
3143 	    if ( i<=smax-1 && stemwidths[i]!=0 && stemwidths[i+1]!=0 ) {
3144 		if ( stemwidths[i]>stemwidths[i+1] ) {
3145 		    stemwidths[i] += stemwidths[i+1];
3146 		    stemwidths[i+1] = 0;
3147 		} else {
3148 		    if ( i<=smax-2 && stemwidths[i+2] && stemwidths[i+2]<stemwidths[i+1] ) {
3149 			stemwidths[i+1] += stemwidths[i+2];
3150 			stemwidths[i+2] = 0;
3151 		    }
3152 		    stemwidths[i+1] += stemwidths[i];
3153 		    stemwidths[i] = 0;
3154 		    ++i;
3155 		}
3156 	    }
3157 	}
3158 	for ( i=smin, cnt=0; i<=smax; ++i ) {
3159 	    if ( stemwidths[i]!=0 )
3160 		++cnt;
3161 	}
3162     }
3163     if ( cnt<=12 ) {
3164 	for ( i=smin, cnt=0; i<=smax; ++i ) {
3165 	    if ( stemwidths[i]!=0 ) {
3166 		snaps[cnt] = i;
3167 		cnts[cnt++] = stemwidths[i];
3168 	    }
3169 	}
3170     } else { real firstbiggest=0;
3171 	for ( cnt = 0; cnt<12; ++cnt ) {
3172 	    int biggesti=0;
3173 	    real biggest=0;
3174 	    for ( i=smin; i<=smax; ++i ) {
3175 		if ( stemwidths[i]>biggest ) { biggest = stemwidths[i]; biggesti=i; }
3176 	    }
3177 	    /* array must be sorted */
3178 	    if ( biggest<firstbiggest/6 )
3179 	break;
3180 	    for ( j=0; j<cnt; ++j )
3181 		if ( snaps[j]>biggesti )
3182 	    break;
3183 	    for ( k=cnt-1; k>=j; --k ) {
3184 		snaps[k+1] = snaps[k];
3185 		cnts[k+1]=cnts[k];
3186 	    }
3187 	    snaps[j] = biggesti;
3188 	    cnts[j] = biggest;
3189 	    stemwidths[biggesti] = 0;
3190 	    if ( firstbiggest==0 ) firstbiggest = biggest;
3191 	}
3192     }
3193     for ( ; cnt<12; ++cnt ) {
3194 	snaps[cnt] = 0;
3195 	cnts[cnt] = 0;
3196     }
3197 }
3198 
FindHStems(SplineFont * sf,real snaps[12],real cnt[12])3199 void FindHStems( SplineFont *sf, real snaps[12], real cnt[12]) {
3200     FigureStems(sf,snaps,cnt,1);
3201 }
3202 
FindVStems(SplineFont * sf,real snaps[12],real cnt[12])3203 void FindVStems( SplineFont *sf, real snaps[12], real cnt[12]) {
3204     FigureStems(sf,snaps,cnt,0);
3205 }
3206 
IsFlexSmooth(SplinePoint * sp)3207 static int IsFlexSmooth(SplinePoint *sp) {
3208     BasePoint nvec, pvec;
3209     double proj_same, proj_normal;
3210 
3211     if ( sp->nonextcp || sp->noprevcp )
3212 return( false );	/* No continuity of slopes */
3213 
3214     nvec.x = sp->nextcp.x - sp->me.x; nvec.y = sp->nextcp.y - sp->me.y;
3215     pvec.x = sp->me.x - sp->prevcp.x; pvec.y = sp->me.y - sp->prevcp.y;
3216 
3217     /* Avoid cases where the slopes are 180 out of phase */
3218     if ( (proj_same   = nvec.x*pvec.x + nvec.y*pvec.y)<=0 )
3219 return( false );
3220     if ( (proj_normal = nvec.x*pvec.y - nvec.y*pvec.x)<0 )
3221 	proj_normal = -proj_normal;
3222 
3223     /* Something is smooth if the normal projection is 0. Let's allow for */
3224     /*  some rounding errors */
3225     if ( proj_same >= 16*proj_normal )
3226 return( true );
3227 
3228 return( false );
3229 }
3230 
_SplineCharIsFlexible(SplineChar * sc,int layer,int blueshift)3231 static int _SplineCharIsFlexible(SplineChar *sc, int layer, int blueshift) {
3232     /* Need two splines
3233 	outer endpoints have same x (or y) values
3234 	inner point must be less than 20 horizontal (v) units from the outer points
3235 	inner point must also be less than BlueShift units (defaults to 7=>6)
3236 	    (can increase BlueShift up to 21)
3237 	the inner point must be a local extremum
3238 	the inner point's cps must be at the x (or y) value as the extremum
3239 		(I think)
3240     */
3241     /* We want long, nearly straight stems. If the end-points should not have
3242       continuous slopes, or if they do, they must be horizontal/vertical.
3243       This is an heuristic requirement, not part of Adobe's spec.
3244     */
3245     SplineSet *spl;
3246     SplinePoint *sp, *np, *pp;
3247     int max=0, val;
3248     RefChar *r;
3249 
3250     if ( sc==NULL )
3251 return(false);
3252 
3253     for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
3254 	if ( spl->first->prev==NULL ) {
3255 	    /* Mark everything on the open path as inflexible */
3256 	    sp=spl->first;
3257 	    while ( 1 ) {
3258 		sp->flexx = sp->flexy = false;
3259 		if ( sp->next==NULL )
3260 	    break;
3261 		sp = sp->next->to;
3262 	    }
3263     continue;				/* Ignore open paths */
3264 	}
3265 	sp=spl->first;
3266 	do {
3267 	    if ( sp->next==NULL || sp->prev==NULL )
3268 	break;
3269 	    np = sp->next->to;
3270 	    pp = sp->prev->from;
3271 	    if ( !pp->flexx && !pp->flexy ) {
3272 		sp->flexy = sp->flexx = 0;
3273 		val = 0;
3274 		if ( RealNear(sp->nextcp.x,sp->me.x) &&
3275 			RealNear(sp->prevcp.x,sp->me.x) &&
3276 			RealNear(np->me.x,pp->me.x) &&
3277 			!RealNear(np->me.x,sp->me.x) &&
3278 			(!IsFlexSmooth(pp) || RealNear(pp->nextcp.x,pp->me.x)) &&
3279 			(!IsFlexSmooth(np) || RealNear(np->prevcp.x,np->me.x)) &&
3280 			np->me.x-sp->me.x < blueshift &&
3281 			np->me.x-sp->me.x > -blueshift ) {
3282 		    if ( (np->me.x>sp->me.x &&
3283 			    np->prevcp.x<=np->me.x && np->prevcp.x>=sp->me.x &&
3284 			    pp->nextcp.x<=pp->me.x && pp->prevcp.x>=sp->me.x ) ||
3285 			  (np->me.x<sp->me.x &&
3286 			    np->prevcp.x>=np->me.x && np->prevcp.x<=sp->me.x &&
3287 			    pp->nextcp.x>=pp->me.x && pp->prevcp.x<=sp->me.x )) {
3288 			sp->flexx = true;
3289 			val = np->me.x-sp->me.x;
3290 		    }
3291 		}
3292 		if ( RealNear(sp->nextcp.y,sp->me.y) &&
3293 			RealNear(sp->prevcp.y,sp->me.y) &&
3294 			RealNear(np->me.y,pp->me.y) &&
3295 			!RealNear(np->me.y,sp->me.y) &&
3296 			(!IsFlexSmooth(pp) || RealNear(pp->nextcp.y,pp->me.y)) &&
3297 			(!IsFlexSmooth(np) || RealNear(np->prevcp.y,np->me.y)) &&
3298 			np->me.y-sp->me.y < blueshift &&
3299 			np->me.y-sp->me.y > -blueshift ) {
3300 		    if ( (np->me.y>sp->me.y &&
3301 			    np->prevcp.y<=np->me.y && np->prevcp.y>=sp->me.y &&
3302 			    pp->nextcp.y<=pp->me.y && pp->nextcp.y>=sp->me.y ) ||
3303 			  (np->me.y<sp->me.y &&
3304 			    np->prevcp.y>=np->me.y && np->prevcp.y<=sp->me.y &&
3305 			    pp->nextcp.y>=pp->me.y && pp->nextcp.y<=sp->me.y )) {
3306 			sp->flexy = true;
3307 			val = np->me.y-sp->me.y;
3308 		    }
3309 		}
3310 		if ( val<0 ) val = -val;
3311 		if ( val>max ) max = val;
3312 	    }
3313 	    sp = np;
3314 	} while ( sp!=spl->first );
3315     }
3316     sc->layers[layer].anyflexes = max>0;
3317     if ( max==0 )
3318 	for ( r = sc->layers[layer].refs; r!=NULL ; r=r->next )
3319 	    if ( r->sc->layers[layer].anyflexes ) {
3320 		sc->layers[layer].anyflexes = true;
3321 	break;
3322 	    }
3323 return( max );
3324 }
3325 
MatchFlexes(MMSet * mm,int layer,int opos)3326 static int MatchFlexes(MMSet *mm,int layer,int opos) {
3327     int any=false, i;
3328     SplineSet *spl[16];
3329     SplinePoint *sp[16];
3330     int mismatchx, mismatchy;
3331 
3332     for ( i=0; i<mm->instance_count; ++i )
3333 	if ( opos<mm->instances[i]->glyphcnt && mm->instances[i]->glyphs[opos]!=NULL )
3334 	    spl[i] = mm->instances[i]->glyphs[opos]->layers[layer].splines;
3335 	else
3336 	    spl[i] = NULL;
3337     while ( spl[0]!=NULL ) {
3338 	for ( i=0; i<mm->instance_count; ++i )
3339 	    if ( spl[i]!=NULL )
3340 		sp[i] = spl[i]->first;
3341 	    else
3342 		sp[i] = NULL;
3343 	while ( sp[0]!=NULL ) {
3344 	    mismatchx = mismatchy = false;
3345 	    for ( i=1 ; i<mm->instance_count; ++i ) {
3346 		if ( sp[i]==NULL )
3347 		    mismatchx = mismatchy = true;
3348 		else {
3349 		    if ( sp[i]->flexx != sp[0]->flexx )
3350 			mismatchx = true;
3351 		    if ( sp[i]->flexy != sp[0]->flexy )
3352 			mismatchy = true;
3353 		}
3354 	    }
3355 	    if ( mismatchx || mismatchy ) {
3356 		for ( i=0 ; i<mm->instance_count; ++i ) if ( sp[i]!=NULL ) {
3357 		    if ( mismatchx ) sp[i]->flexx = false;
3358 		    if ( mismatchy ) sp[i]->flexy = false;
3359 		}
3360 	    }
3361 	    if ( sp[0]->flexx || sp[0]->flexy )
3362 		any = true;
3363 	    for ( i=0 ; i<mm->instance_count; ++i ) if ( sp[i]!=NULL ) {
3364 		if ( sp[i]->next==NULL ) sp[i] = NULL;
3365 		else sp[i] = sp[i]->next->to;
3366 	    }
3367 	    if ( sp[0] == spl[0]->first )
3368 	break;
3369 	}
3370 	for ( i=0; i<mm->instance_count; ++i )
3371 	    if ( spl[i]!=NULL )
3372 		spl[i] = spl[i]->next;
3373     }
3374 return( any );
3375 }
3376 
SplineCharIsFlexible(SplineChar * sc,int layer)3377 int SplineCharIsFlexible(SplineChar *sc,int layer) {
3378     char *pt;
3379     int blueshift;
3380     int i;
3381     MMSet *mm;
3382 
3383     pt = PSDictHasEntry(sc->parent->private,"BlueShift");
3384     blueshift = 7;		/* use default value here */
3385     if ( pt!=NULL ) {
3386 	blueshift = strtol(pt,NULL,10);
3387 	if ( blueshift>21 ) blueshift = 21;
3388     } else if ( PSDictHasEntry(sc->parent->private,"BlueValues")!=NULL )
3389 	blueshift = 7;
3390     if ( sc->parent->mm==NULL )
3391 return( _SplineCharIsFlexible(sc,layer,blueshift));
3392 
3393     mm = sc->parent->mm;
3394     for ( i = 0; i<mm->instance_count; ++i )
3395 	if ( sc->orig_pos<mm->instances[i]->glyphcnt && mm->instances[i]->glyphs[sc->orig_pos]!=NULL )
3396 	    _SplineCharIsFlexible(mm->instances[i]->glyphs[sc->orig_pos],layer,blueshift);
3397 return( MatchFlexes(mm,layer,sc->orig_pos));
3398 }
3399 
SCUnflex(SplineChar * sc,int layer)3400 static void SCUnflex(SplineChar *sc, int layer) {
3401     SplineSet *spl;
3402     SplinePoint *sp;
3403 
3404     for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
3405 	/* Mark everything on the path as inflexible */
3406 	sp=spl->first;
3407 	while ( 1 ) {
3408 	    sp->flexx = sp->flexy = false;
3409 	    if ( sp->next==NULL )
3410 	break;
3411 	    sp = sp->next->to;
3412 	    if ( sp==spl->first )
3413 	break;
3414 	}
3415     }
3416     sc->layers[layer].anyflexes = false;
3417 }
3418 
FlexDependents(SplineChar * sc,int layer)3419 static void FlexDependents(SplineChar *sc,int layer) {
3420     struct splinecharlist *scl;
3421 
3422     sc->layers[layer].anyflexes = true;
3423     for ( scl = sc->dependents; scl!=NULL; scl=scl->next )
3424 	FlexDependents(scl->sc,layer);
3425 }
3426 
SplineFontIsFlexible(SplineFont * sf,int layer,int flags)3427 int SplineFontIsFlexible(SplineFont *sf,int layer, int flags) {
3428     int i;
3429     int max=0, val;
3430     char *pt;
3431     int blueshift;
3432     /* if the return value is bigger than 6 and we don't have a BlueShift */
3433     /*  then we must set BlueShift to ret+1 before saving private dictionary */
3434     /* If the first point in a spline set is flexible, then we must rotate */
3435     /*  the splineset */
3436 
3437     if ( flags&(ps_flag_nohints|ps_flag_noflex)) {
3438 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
3439 	    SCUnflex(sf->glyphs[i],layer);
3440 return( 0 );
3441     }
3442 
3443     pt = PSDictHasEntry(sf->private,"BlueShift");
3444     blueshift = 21;		/* maximum posible flex, not default */
3445     if ( pt!=NULL ) {
3446 	blueshift = strtol(pt,NULL,10);
3447 	if ( blueshift>21 ) blueshift = 21;
3448     } else if ( PSDictHasEntry(sf->private,"BlueValues")!=NULL )
3449 	blueshift = 7;	/* The BlueValues array may depend on BlueShift having its default value */
3450 
3451     for ( i=0; i<sf->glyphcnt; ++i )
3452 	if ( sf->glyphs[i]!=NULL ) if ( sf->glyphs[i]!=NULL ) {
3453 	    val = _SplineCharIsFlexible(sf->glyphs[i],layer,blueshift);
3454 	    if ( val>max ) max = val;
3455 	    if ( sf->glyphs[i]->layers[layer].anyflexes )
3456 		FlexDependents(sf->glyphs[i],layer);
3457 	}
3458 return( max );
3459 }
3460 
3461 
3462