beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / luafontloader / fontforge / fontforge / autohint.c
blob6619bd1f76204c205a9fb92928e2554c4f988f1c
1 /* Copyright (C) 2000-2008 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:
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
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.
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.
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.
27 #include "pfaedit.h"
28 #include <stdio.h>
29 #include <math.h>
30 #include "splinefont.h"
31 #include "stemdb.h"
32 #include <utype.h>
33 #include <chardata.h>
34 #include "edgelist.h"
36 /* to create a type 1 font we must come up with the following entries for the
37 private dictionary:
38 BlueValues -- an array of 2n entries where Blue[2i]<Blue[2i+1] max n=7, Blue[i]>0
39 OtherBlues -- (optional) OtherBlue[i]<0
40 (blue zones should be at least 3 units appart)
41 StdHW -- (o) array with one entry, standard hstem height
42 StdVW -- (o) ditto vstem width
43 StemSnapH -- (o) up to 12 numbers containing various hstem heights (includes StdHW), small widths first
44 StemSnapV -- (o) ditto, vstem
45 This file has routines to figure out at least some of these
47 Also other routines to guess at good per-character hints
50 static void AddBlue(real val, real array[5], int force) {
51 val = rint(val);
52 if ( !force && (val<array[0]-array[1] || val >array[0]+array[1] ))
53 return; /* Outside of one sd */
54 if ( array[3]==0 && array[4]==0 )
55 array[3] = array[4] = val;
56 else if ( val>array[4] )
57 array[4] = val;
58 else if ( val<array[3] )
59 array[3] = val;
62 static void MergeZones(real zone1[5], real zone2[5]) {
63 if ( zone1[2]!=0 && zone2[2]!=0 &&
64 ((zone1[4]+3>zone2[3] && zone1[3]<=zone2[3]) ||
65 (zone2[4]+3>zone1[3] && zone2[3]<=zone1[3]) )) {
66 if (( zone2[0]<zone1[0]-zone1[1] || zone2[0] >zone1[0]+zone1[1] ) &&
67 ( zone1[0]<zone2[0]-zone2[1] || zone1[0] >zone2[0]+zone2[1] ))
68 /* the means of the zones are too far appart, don't merge em */;
69 else {
70 if ( zone1[0]<zone2[0] ) zone2[0] = zone1[0];
71 if ( zone1[1]>zone2[1] ) zone2[1] = zone1[1];
73 zone1[2] = 0;
77 /* I can deal with latin, greek and cyrillic because the they all come from */
78 /* the same set of letter shapes and have all evolved together and have */
79 /* various common features (ascenders, descenders, lower case, etc.). Other */
80 /* scripts don't fit */
81 void FindBlues( SplineFont *sf, int layer, real blues[14], real otherblues[10]) {
82 real caph[5], xh[5], ascenth[5], digith[5], descenth[5], base[5];
83 real otherdigits[5];
84 int i, j, k;
85 DBounds b;
87 /* Go through once to get some idea of the average value so we can weed */
88 /* out undesireables */
89 caph[0] = caph[1] = caph[2] = xh[0] = xh[1] = xh[2] = 0;
90 ascenth[0] = ascenth[1] = ascenth[2] = digith[0] = digith[1] = digith[2] = 0;
91 descenth[0] = descenth[1] = descenth[2] = base[0] = base[1] = base[2] = 0;
92 otherdigits[0] = otherdigits[1] = otherdigits[2] = 0;
93 for ( i=0; i<sf->glyphcnt; ++i ) {
94 if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->layers[layer].splines!=NULL ) {
95 int enc = sf->glyphs[i]->unicodeenc;
96 #ifndef LUA_FF_LIB
97 const unichar_t *upt;
98 #endif
99 if ( enc<0x10000 && isalnum(enc) &&
100 ((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf ||
101 enc==0x131 ||
102 (enc>=0x391 && enc<=0x3f3 ) ||
103 (enc>=0x400 && enc<=0x4e9 ) )) {
104 /* no accented characters (or ligatures) */
105 #ifndef LUA_FF_LIB
106 if ( unicode_alternates[enc>>8]!=NULL &&
107 (upt =unicode_alternates[enc>>8][enc&0xff])!=NULL &&
108 upt[1]!='\0' )
109 continue;
110 #endif
111 SplineCharFindBounds(sf->glyphs[i],&b);
112 if ( b.miny==0 && b.maxy==0 )
113 continue;
114 if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' ||
115 enc==0xfe ||
116 enc==0x3c1 /* rho */ ||
117 enc==0x3c6 /* phi */ ||
118 enc==0x3c7 /* chi */ ||
119 enc==0x3c8 /* psi */ ||
120 enc==0x440 /* cyr er */ ||
121 enc==0x443 /* cyr u */ ||
122 enc==0x444 /* cyr ef */) {
123 descenth[0] += b.miny;
124 descenth[1] += b.miny*b.miny;
125 ++descenth[2];
126 } else if ( enc=='x' || enc=='r' || enc=='o' || enc=='e' ||
127 enc=='s' || enc=='c' || enc=='h' || enc=='k' ||
128 enc=='l' || enc=='m' || enc=='n' ||
129 enc==0x3b5 /* epsilon */ ||
130 enc==0x3b9 /* iota */ ||
131 enc==0x3ba /* kappa */ ||
132 enc==0x3bf /* omicron */ ||
133 enc==0x3c3 /* sigma */ ||
134 enc==0x3c5 /* upsilon */ ||
135 enc==0x430 /* cyr a */ ||
136 enc==0x432 /* cyr ve */ ||
137 enc==0x433 /* cyr ge */ ||
138 enc==0x435 /* cyr e */ ||
139 enc==0x436 /* cyr zhe */ ||
140 enc==0x438 /* cyr i */ ||
141 enc==0x43a /* cyr ka */ ||
142 enc==0x43d /* cyr en */ ||
143 enc==0x43e /* cyr o */ ||
144 enc==0x441 /* cyr es */ ||
145 enc==0x445 /* cyr ha */ ||
146 enc==0x447 /* cyr che */ ||
147 enc==0x448 /* cyr sha */ ||
148 enc==0x44f /* cyr ya */ ){
149 base[0] += b.miny;
150 base[1] += b.miny*b.miny;
151 ++base[2];
153 /* careful of lowercase digits, 6 and 8 should be ascenders */
154 if ( enc=='6' || enc=='8' ) {
155 digith[0] += b.maxy;
156 digith[1] += b.maxy*b.maxy;
157 ++digith[2];
158 } else if ( enc<0x10000 && isdigit(enc) ) {
159 otherdigits[0] += b.maxy;
160 otherdigits[1] += b.maxy*b.maxy;
161 ++otherdigits[2];
162 } else if ( enc<0x10000 && isupper(enc) && enc!=0x462 && enc!=0x490 ) {
163 caph[0] += b.maxy;
164 caph[1] += b.maxy*b.maxy;
165 ++caph[2];
166 } else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' ||
167 enc == 'l' || enc==0xf0 || enc==0xfe || enc == 0xdf ||
168 enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb ||
169 enc == 0x3be ||
170 enc == 0x431 /* cyr be */ /* || enc == 0x444 - ef may have varible height */) {
171 ascenth[0] += b.maxy;
172 ascenth[1] += b.maxy*b.maxy;
173 ++ascenth[2];
174 } else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' ||
175 enc=='u' || enc=='v' || enc=='w' || enc=='x' || enc=='y' ||
176 enc=='z' ||
177 enc==0x3b5 /* epsilon */ ||
178 enc==0x3b9 /* iota */ ||
179 enc==0x3ba /* kappa */ ||
180 enc==0x3bc /* mu */ ||
181 enc==0x3bd /* nu */ ||
182 enc==0x3bf /* omicron */ ||
183 enc==0x3c0 /* pi */ ||
184 enc==0x3c1 /* rho */ ||
185 enc==0x3c5 /* upsilon */ ||
186 enc==0x433 /* cyr ge */ ||
187 enc==0x435 /* cyr e */ ||
188 enc==0x436 /* cyr zhe */ ||
189 enc==0x438 /* cyr i */ ||
190 enc==0x43b /* cyr el */ ||
191 enc==0x43d /* cyr en */ ||
192 enc==0x43e /* cyr o */ ||
193 enc==0x43f /* cyr pe */ ||
194 enc==0x440 /* cyr er */ ||
195 enc==0x441 /* cyr es */ ||
196 enc==0x442 /* cyr te */ ||
197 enc==0x443 /* cyr u */ ||
198 enc==0x445 /* cyr ha */ ||
199 enc==0x446 /* cyr tse */ ||
200 enc==0x447 /* cyr che */ ||
201 enc==0x448 /* cyr sha */ ||
202 enc==0x449 /* cyr shcha */ ||
203 enc==0x44a /* cyr hard sign */ ||
204 enc==0x44b /* cyr yery */ ||
205 enc==0x44c /* cyr soft sign */ ||
206 enc==0x44d /* cyr reversed e */ ||
207 enc==0x44f /* cyr ya */ ) {
208 xh[0] += b.maxy;
209 xh[1] += b.maxy*b.maxy;
210 ++xh[2];
214 if ( !ff_progress_next())
215 break;
217 if ( otherdigits[2]>0 && digith[2]>0 ) {
218 if ( otherdigits[0]/otherdigits[2] >= .95*digith[0]/digith[2] ) {
219 /* all digits are about the same height, not lowercase */
220 digith[0] += otherdigits[0];
221 digith[1] += otherdigits[1];
222 digith[2] += otherdigits[2];
226 if ( xh[2]>1 ) {
227 xh[1] = sqrt((xh[1]-xh[0]*xh[0]/xh[2])/(xh[2]-1));
228 xh[0] /= xh[2];
230 if ( ascenth[2]>1 ) {
231 ascenth[1] = sqrt((ascenth[1]-ascenth[0]*ascenth[0]/ascenth[2])/(ascenth[2]-1));
232 ascenth[0] /= ascenth[2];
234 if ( caph[2]>1 ) {
235 caph[1] = sqrt((caph[1]-caph[0]*caph[0]/caph[2])/(caph[2]-1));
236 caph[0] /= caph[2];
238 if ( digith[2]>1 ) {
239 digith[1] = sqrt((digith[1]-digith[0]*digith[0]/digith[2])/(digith[2]-1));
240 digith[0] /= digith[2];
242 if ( base[2]>1 ) {
243 base[1] = sqrt((base[1]-base[0]*base[0]/base[2])/(base[2]-1));
244 base[0] /= base[2];
246 if ( descenth[2]>1 ) {
247 descenth[1] = sqrt((descenth[1]-descenth[0]*descenth[0]/descenth[2])/(descenth[2]-1));
248 descenth[0] /= descenth[2];
251 /* we'll accept values between +/- 1sd of the mean */
252 /* array[0] == mean, array[1] == sd, array[2] == cnt, array[3]=min, array[4]==max */
253 if ( base[0]+base[1]<0 ) base[1] = -base[0]; /* Make sure 0 is within the base bluezone */
254 caph[3] = caph[4] = 0;
255 xh[3] = xh[4] = 0;
256 ascenth[3] = ascenth[4] = 0;
257 digith[3] = digith[4] = 0;
258 descenth[3] = descenth[4] = 0;
259 base[3] = base[4] = 0;
260 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
261 int enc = sf->glyphs[i]->unicodeenc;
262 #ifndef LUA_FF_LIB
263 const unichar_t *upt;
264 #endif
265 if ( enc<0x10000 && isalnum(enc) &&
266 ((enc>=32 && enc<128 ) || enc == 0xfe || enc==0xf0 || enc==0xdf ||
267 (enc>=0x391 && enc<=0x3f3 ) ||
268 (enc>=0x400 && enc<=0x4e9 ) )) {
269 /* no accented characters (or ligatures) */
270 #ifndef LUA_FF_LIB
271 if ( unicode_alternates[enc>>8]!=NULL &&
272 (upt =unicode_alternates[enc>>8][enc&0xff])!=NULL &&
273 upt[1]!='\0' )
274 continue;
275 #endif
276 SplineCharFindBounds(sf->glyphs[i],&b);
277 if ( b.miny==0 && b.maxy==0 )
278 continue;
279 if ( enc=='g' || enc=='j' || enc=='p' || enc=='q' || enc=='y' ||
280 enc==0xfe || enc == 0x3c6 || enc==0x3c8 ||
281 enc==0x440 || enc==0x443 || enc==0x444) {
282 AddBlue(b.miny,descenth,false);
283 } else {
284 /* O and o get forced into the baseline blue value even if they*/
285 /* are beyond 1 sd */
286 AddBlue(b.miny,base,enc=='O' || enc=='o');
288 if ( enc<0x10000 && isdigit(enc)) {
289 AddBlue(b.maxy,digith,false);
290 } else if ( enc<0x10000 && isupper(enc)) {
291 AddBlue(b.maxy,caph,enc=='O');
292 } else if ( enc=='b' || enc=='d' || enc=='f' || enc=='h' || enc=='k' ||
293 enc == 'l' || enc=='t' || enc==0xf0 || enc==0xfe || enc == 0xdf ||
294 enc == 0x3b2 || enc==0x3b6 || enc==0x3b8 || enc==0x3bb ||
295 enc == 0x3be ||
296 enc == 0x431 ) {
297 AddBlue(b.maxy,ascenth,false);
298 } else if ( enc=='c' || enc=='e' || enc=='o' || enc=='s' || enc=='u' ||
299 enc=='u' || enc=='v' || enc=='w' || enc=='x' || enc=='y' ||
300 enc=='z' ||
301 enc==0x3b5 /* epsilon */ ||
302 enc==0x3b9 /* iota */ ||
303 enc==0x3ba /* kappa */ ||
304 enc==0x3bc /* mu */ ||
305 enc==0x3bd /* nu */ ||
306 enc==0x3bf /* omicron */ ||
307 enc==0x3c0 /* pi */ ||
308 enc==0x3c1 /* rho */ ||
309 enc==0x3c5 /* upsilon */ ||
310 enc==0x433 /* cyr ge */ ||
311 enc==0x435 /* cyr e */ ||
312 enc==0x436 /* cyr zhe */ ||
313 enc==0x438 /* cyr i */ ||
314 enc==0x43b /* cyr el */ ||
315 enc==0x43d /* cyr en */ ||
316 enc==0x43e /* cyr o */ ||
317 enc==0x43f /* cyr pe */ ||
318 enc==0x440 /* cyr er */ ||
319 enc==0x441 /* cyr es */ ||
320 enc==0x442 /* cyr te */ ||
321 enc==0x443 /* cyr u */ ||
322 enc==0x445 /* cyr ha */ ||
323 enc==0x446 /* cyr tse */ ||
324 enc==0x447 /* cyr che */ ||
325 enc==0x448 /* cyr sha */ ||
326 enc==0x449 /* cyr shcha */ ||
327 enc==0x44a /* cyr hard sign */ ||
328 enc==0x44b /* cyr yery */ ||
329 enc==0x44c /* cyr soft sign */ ||
330 enc==0x44d /* cyr reversed e */ ||
331 enc==0x44f /* cyr ya */ ) {
332 AddBlue(b.maxy,xh,enc=='o' || enc=='x');
337 /* the descent blue zone merges into the base zone */
338 MergeZones(descenth,base);
339 MergeZones(xh,base);
340 MergeZones(ascenth,caph);
341 MergeZones(digith,caph);
342 MergeZones(xh,caph);
343 MergeZones(ascenth,digith);
344 MergeZones(xh,digith);
346 if ( otherblues!=NULL )
347 for ( i=0; i<10; ++i )
348 otherblues[i] = 0;
349 for ( i=0; i<14; ++i )
350 blues[i] = 0;
352 if ( otherblues!=NULL && descenth[2]!=0 ) {
353 otherblues[0] = descenth[3];
354 otherblues[1] = descenth[4];
356 i = 0;
357 if ( base[2]==0 && (xh[2]!=0 || ascenth[2]!=0 || caph[2]!=0 || digith[2]!=0 )) {
358 /* base line blue value must be present if any other value is */
359 /* make one up if we don't have one */
360 blues[0] = -20;
361 blues[1] = 0;
362 i = 2;
363 } else if ( base[2]!=0 ) {
364 blues[0] = base[3];
365 blues[1] = base[4];
366 i = 2;
368 if ( xh[2]!=0 ) {
369 blues[i++] = xh[3];
370 blues[i++] = xh[4];
372 if ( caph[2]!=0 ) {
373 blues[i++] = caph[3];
374 blues[i++] = caph[4];
376 if ( digith[2]!=0 ) {
377 blues[i++] = digith[3];
378 blues[i++] = digith[4];
380 if ( ascenth[2]!=0 ) {
381 blues[i++] = ascenth[3];
382 blues[i++] = ascenth[4];
385 for ( j=0; j<i; j+=2 ) for ( k=j+2; k<i; k+=2 ) {
386 if ( blues[j]>blues[k] ) {
387 real temp = blues[j];
388 blues[j]=blues[k];
389 blues[k] = temp;
390 temp = blues[j+1];
391 blues[j+1] = blues[k+1];
392 blues[k+1] = temp;
397 void ElFreeEI(EIList *el) {
398 EI *e, *next;
400 for ( e = el->edges; e!=NULL; e = next ) {
401 next = e->next;
402 free(e);
406 static int EIAddEdge(Spline *spline, real tmin, real tmax, EIList *el) {
407 EI *new = gcalloc(1,sizeof(EI));
408 real min, max, temp;
409 Spline1D *s;
410 real dxdtmin, dxdtmax, dydtmin, dydtmax;
412 new->spline = spline;
413 new->tmin = tmin;
414 new->tmax = tmax;
416 s = &spline->splines[1];
417 if (( dydtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dydtmin = -dydtmin;
418 if (( dydtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dydtmax = -dydtmax;
419 s = &spline->splines[0];
420 if (( dxdtmin = (3*s->a*tmin + 2*s->b)*tmin + s->c )<0 ) dxdtmin = -dxdtmin;
421 if (( dxdtmax = (3*s->a*tmax + 2*s->b)*tmax + s->c )<0 ) dxdtmax = -dxdtmax;
423 /*s = &spline->splines[0];*/
424 min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d;
425 max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d;
426 if ( tmax==1 ) max = spline->to->me.x; /* beware rounding errors */
427 if ( !el->leavetiny && floor(min)==floor(max) ) { /* If it doesn't cross a pixel boundary then it might as well be vertical */
428 if ( tmin==0 ) max = min;
429 else if ( tmax==1 ) min = max;
430 else max = min;
432 if ( min==max )
433 new->vert = true;
434 else if ( min<max )
435 new->hup = true;
436 else {
437 temp = min; min = max; max=temp;
439 if ( !el->leavetiny && min+1>max ) new->almostvert = true;
440 if ( 40*dxdtmin<dydtmin ) new->vertattmin = true;
441 if ( 40*dxdtmax<dydtmax ) new->vertattmax = true;
442 /*if ( new->vertattmin && new->vertattmax && s->a==0 && s->b==0 ) new->almostvert = true;*/
443 new->coordmin[0] = min; new->coordmax[0] = max;
444 if ( el->coordmin[0]>min )
445 el->coordmin[0] = min;
446 if ( el->coordmax[0]<max )
447 el->coordmax[0] = max;
449 s = &spline->splines[1];
450 min = ((s->a * tmin + s->b)* tmin + s->c)* tmin + s->d;
451 max = ((s->a * tmax + s->b)* tmax + s->c)* tmax + s->d;
452 if ( tmax==1 ) max = spline->to->me.y;
453 if ( !el->leavetiny && floor(min)==floor(max) ) { /* If it doesn't cross a pixel boundary then it might as well be horizontal */
454 if ( tmin==0 ) max = min;
455 else if ( tmax==1 ) min = max;
456 else max = min;
458 if ( min==max )
459 new->hor = true;
460 else if ( min<max )
461 new->vup = true;
462 else {
463 temp = min; min = max; max=temp;
465 if ( !el->leavetiny && min+1>max ) new->almosthor = true;
466 if ( 40*dydtmin<dxdtmin ) new->horattmin = true;
467 if ( 40*dydtmax<dxdtmax ) new->horattmax = true;
468 /*if ( new->horattmin && new->horattmax && s->a==0 && s->b==0 ) new->almosthor = true;*/
469 new->coordmin[1] = min; new->coordmax[1] = max;
470 if ( el->coordmin[1]>min )
471 el->coordmin[1] = min;
472 if ( el->coordmax[1]<max )
473 el->coordmax[1] = max;
475 if ( new->hor && new->vert ) {
476 /* This spline is too small for us to notice */
477 free(new);
478 return( false );
479 } else {
480 new->next = el->edges;
481 el->edges = new;
483 if ( el->splinelast!=NULL )
484 el->splinelast->splinenext = new;
485 el->splinelast = new;
486 if ( el->splinefirst==NULL )
487 el->splinefirst = new;
489 return( true );
493 static void EIAddSpline(Spline *spline, EIList *el) {
494 extended ts[6], temp;
495 int i, j, base, last;
497 ts[0] = 0; ts[5] = 1.0;
498 SplineFindExtrema(&spline->splines[0],&ts[1],&ts[2]);
499 SplineFindExtrema(&spline->splines[1],&ts[3],&ts[4]);
500 /* avoid teeny tiny segments, they just confuse us */
501 SplineRemoveExtremaTooClose(&spline->splines[0],&ts[1],&ts[2]);
502 SplineRemoveExtremaTooClose(&spline->splines[1],&ts[3],&ts[4]);
503 for ( i=0; i<4; ++i ) for ( j=i+1; j<5; ++j ) {
504 if ( ts[i]>ts[j] ) {
505 temp = ts[i];
506 ts[i] = ts[j];
507 ts[j] = temp;
510 for ( base=0; ts[base]==-1; ++base);
511 for ( i=5; i>base ; --i ) {
512 if ( ts[i]==ts[i-1] ) {
513 for ( j=i-1; j>base; --j )
514 ts[j] = ts[j-1];
515 ts[j] = -1;
516 ++base;
519 last = base;
520 for ( i=base; i<5 ; ++i )
521 if ( EIAddEdge(spline,ts[last],ts[i+1],el) )
522 last = i+1;
525 void ELFindEdges(SplineChar *sc, EIList *el) {
526 Spline *spline, *first;
527 SplineSet *spl;
529 el->sc = sc;
530 if ( sc->layers[el->layer].splines==NULL )
531 return;
533 el->coordmin[0] = el->coordmax[0] = sc->layers[el->layer].splines->first->me.x;
534 el->coordmin[1] = el->coordmax[1] = sc->layers[el->layer].splines->first->me.y;
536 for ( spl = sc->layers[el->layer].splines; spl!=NULL; spl = spl->next ) if ( spl->first->prev!=NULL && spl->first->prev->from!=spl->first ) {
537 first = NULL;
538 for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
539 EIAddSpline(spline,el);
540 if ( first==NULL ) first = spline;
542 if ( el->splinefirst!=NULL && spl->first->prev!=NULL )
543 el->splinelast->splinenext = el->splinefirst;
544 el->splinelast = NULL; el->splinefirst = NULL;
548 static int IsBiggerSlope(EI *test, EI *base, int major) {
549 int other = !major;
550 real tdo, tdm, bdo, bdm, t;
552 if (( major && test->vup ) || (!major && test->hup))
553 t = test->tmin;
554 else
555 t = test->tmax;
556 tdm = (3*test->spline->splines[major].a*t + 2*test->spline->splines[major].b)*t + test->spline->splines[major].c;
557 tdo = (3*test->spline->splines[other].a*t + 2*test->spline->splines[other].b)*t + test->spline->splines[other].c;
559 if (( major && base->vup ) || (!major && base->hup))
560 t = base->tmin;
561 else
562 t = base->tmax;
563 bdm = (3*base->spline->splines[major].a*t + 2*base->spline->splines[major].b)*t + base->spline->splines[major].c;
564 bdo = (3*base->spline->splines[other].a*t + 2*base->spline->splines[other].b)*t + base->spline->splines[other].c;
566 if ( tdm==0 && bdm==0 )
567 return( tdo > bdo );
568 if ( tdo==0 )
569 return( tdo>0 );
570 else if ( bdo==0 )
571 return( bdo>0 );
573 return( tdo/tdm > bdo/bdm );
576 void ELOrder(EIList *el, int major ) {
577 int other = !major;
578 int pos;
579 EI *ei, *prev, *test;
581 el->low = floor(el->coordmin[major]); el->high = ceil(el->coordmax[major]);
582 el->cnt = el->high-el->low+1;
583 el->ordered = gcalloc(el->cnt,sizeof(EI *));
584 el->ends = gcalloc(el->cnt,1);
586 for ( ei = el->edges; ei!=NULL ; ei=ei->next ) {
587 pos = ceil(ei->coordmax[major])-el->low;
588 el->ends[pos] = true;
589 pos = floor(ei->coordmin[major])-el->low;
590 ei->ocur = (ei->hup == ei->vup)?ei->coordmin[other]:ei->coordmax[other];
591 ei->tcur = ((major && ei->vup) || (!major && ei->hup)) ?
592 ei->tmin: ei->tmax;
593 if ( major ) {
594 ei->up = ei->vup;
595 ei->hv = (ei->vert || ei->almostvert);
596 ei->hvbottom = ei->vup ? ei->vertattmin : ei->vertattmax;
597 ei->hvtop =!ei->vup ? ei->vertattmin : ei->vertattmax;
598 if ( ei->hor || ei->almosthor)
599 continue;
600 } else {
601 ei->up = ei->hup;
602 ei->hv = (ei->hor || ei->almosthor);
603 ei->hvbottom = ei->hup ? ei->horattmin : ei->horattmax;
604 ei->hvtop =!ei->hup ? ei->horattmin : ei->horattmax;
605 if ( ei->vert || ei->almostvert)
606 continue;
608 if ( el->ordered[pos]==NULL || ei->ocur<el->ordered[pos]->ocur ) {
609 ei->ordered = el->ordered[pos];
610 el->ordered[pos] = ei;
611 } else {
612 for ( prev=el->ordered[pos], test = prev->ordered; test!=NULL;
613 prev = test, test = test->ordered ) {
614 if ( test->ocur>ei->ocur ||
615 (test->ocur==ei->ocur && IsBiggerSlope(test,ei,major)))
616 break;
618 ei->ordered = test;
619 prev->ordered = ei;
624 static HintInstance *HIMerge(HintInstance *into, HintInstance *hi) {
625 HintInstance *n, *first = NULL, *last = NULL;
627 while ( into!=NULL && hi!=NULL ) {
628 if ( into->begin<hi->begin ) {
629 n = into;
630 into = into->next;
631 } else {
632 n = hi;
633 hi = hi->next;
635 if ( first==NULL )
636 first = n;
637 else
638 last->next = n;
639 last = n;
641 if ( into!=NULL ) {
642 if ( first==NULL )
643 first = into;
644 else
645 last->next = into;
646 } else if ( hi!=NULL ) {
647 if ( first==NULL )
648 first = hi;
649 else
650 last->next = hi;
652 return( first );
655 StemInfo *HintCleanup(StemInfo *stem,int dosort,int instance_count) {
656 StemInfo *s, *p=NULL, *t, *pt, *sn;
657 int swap;
659 for ( s=stem; s!=NULL; p=s, s=s->next ) {
660 if ( s->width<0 ) {
661 s->start += s->width;
662 s->width = -s->width;
663 s->ghost = true;
665 s->reordered = false;
666 if ( p!=NULL && p->start> s->start )
667 dosort = true;
669 if ( dosort ) {
670 for ( p=NULL, s=stem; s!=NULL; p=s, s = sn ) {
671 sn = s->next;
672 for ( pt=s, t=sn; t!=NULL; pt=t, t=t->next ) {
673 if ( instance_count>1 &&
674 t->u.unblended!=NULL && s->u.unblended!=NULL ) {
675 int temp = UnblendedCompare((*t->u.unblended)[0],(*s->u.unblended)[0],instance_count);
676 if ( temp==0 )
677 swap = UnblendedCompare((*t->u.unblended)[1],(*s->u.unblended)[1],instance_count);
678 else
679 swap = temp<0;
680 } else if ( t->start<s->start )
681 swap=true;
682 else if ( t->start>s->start )
683 swap = false;
684 else
685 swap = (t->width<s->width);
686 if ( swap ) {
687 s->next = t->next;
688 if ( pt==s ) {
689 t->next = s;
690 sn = s;
691 } else {
692 t->next = sn;
693 pt->next = s;
695 if ( p==NULL )
696 stem = t;
697 else
698 p->next = t;
699 pt = s; /* swap s and t */
700 s = t;
701 t = pt;
705 /* Remove duplicates */
706 if ( stem!=NULL ) for ( p=stem, s=stem->next; s!=NULL; s = sn ) {
707 sn = s->next;
708 if ( p->start==s->start && p->width==s->width && p->hintnumber==s->hintnumber ) {
709 p->where = HIMerge(p->where,s->where);
710 s->where = NULL;
711 p->next = sn;
712 StemInfoFree(s);
713 } else
714 p = s;
717 return( stem );
720 real EITOfNextMajor(EI *e, EIList *el, real sought_m ) {
721 /* We want to find t so that Mspline(t) = sought_m */
722 /* the curve is monotonic */
723 Spline1D *msp = &e->spline->splines[el->major];
724 real new_t;
725 real found_m;
726 real t_mmax, t_mmin;
728 if ( msp->a==0 && msp->b==0 ) {
729 if ( msp->c == 0 ) {
730 IError("Hor/Vert line when not expected");
731 return( 0 );
733 new_t = (sought_m-msp->d)/(msp->c);
734 return( new_t );
737 t_mmax = e->up?e->tmax:e->tmin;
738 t_mmin = e->up?e->tmin:e->tmax;
739 /* sought_m += el->low; */
741 while ( 1 ) {
742 new_t = (t_mmin+t_mmax)/2;
743 found_m = ( ((msp->a*new_t+msp->b)*new_t+msp->c)*new_t + msp->d );
744 if ( found_m>sought_m-.001 && found_m<sought_m+.001 )
745 return( new_t );
746 if ( found_m > sought_m ) {
747 t_mmax = new_t;
748 } else {
749 t_mmin = new_t;
751 if ( t_mmax==t_mmin ) {
752 IError("EITOfNextMajor failed! on %s", el->sc!=NULL?el->sc->name:"Unknown" );
753 return( new_t );
758 EI *EIActiveListReorder(EI *active,int *change) {
759 int any;
760 EI *pr, *apt;
762 *change = false;
763 if ( active!=NULL ) {
764 any = true;
765 while ( any ) {
766 any = false;
767 for ( pr=NULL, apt=active; apt->aenext!=NULL; ) {
768 if ( apt->ocur <= apt->aenext->ocur ) {
769 /* still ordered */;
770 pr = apt;
771 apt = apt->aenext;
772 } else if ( pr==NULL ) {
773 active = apt->aenext;
774 apt->aenext = apt->aenext->aenext;
775 active->aenext = apt;
776 *change = true;
777 /* don't need to set any, since this reorder can't disorder the list */
778 pr = active;
779 } else {
780 pr->aenext = apt->aenext;
781 apt->aenext = apt->aenext->aenext;
782 pr->aenext->aenext = apt;
783 any = *change = true;
784 pr = pr->aenext;
789 return( active );
792 EI *EIActiveEdgesRefigure(EIList *el, EI *active,real i,int major, int *_change) {
793 EI *apt, *pr, *npt;
794 int change = false, subchange;
795 int other = !major;
797 /* first remove any entry which doesn't intersect the new scan line */
798 /* (ie. stopped on last line) */
799 for ( pr=NULL, apt=active; apt!=NULL; apt = apt->aenext ) {
800 if ( apt->coordmax[major]<i+el->low ) {
801 if ( pr==NULL )
802 active = apt->aenext;
803 else
804 pr->aenext = apt->aenext;
805 change = true;
806 } else
807 pr = apt;
809 /* then move the active list to the next line */
810 for ( apt=active; apt!=NULL; apt = apt->aenext ) {
811 Spline1D *osp = &apt->spline->splines[other];
812 apt->tcur = EITOfNextMajor(apt,el,i+el->low);
813 apt->ocur = ( ((osp->a*apt->tcur+osp->b)*apt->tcur+osp->c)*apt->tcur + osp->d );
815 /* reorder list */
816 active = EIActiveListReorder(active,&subchange);
817 if ( subchange ) change = true;
819 /* Insert new nodes */
820 if ( el->ordered[(int) i]!=NULL ) change = true;
821 for ( pr=NULL, apt=active, npt=el->ordered[(int) i]; apt!=NULL && npt!=NULL; ) {
822 if ( npt->ocur<apt->ocur ) {
823 npt->aenext = apt;
824 if ( pr==NULL )
825 active = npt;
826 else
827 pr->aenext = npt;
828 pr = npt;
829 npt = npt->ordered;
830 } else {
831 pr = apt;
832 apt = apt->aenext;
835 while ( npt!=NULL ) {
836 npt->aenext = NULL;
837 if ( pr==NULL )
838 active = npt;
839 else
840 pr->aenext = npt;
841 pr = npt;
842 npt = npt->ordered;
844 *_change = change;
845 return( active );
849 int EISkipExtremum(EI *e, real i, int major) {
850 EI *n = e->aenext, *t;
852 if ( n==NULL )
853 return( false );
854 if (
855 (ceil(e->coordmin[major])==i || floor(e->coordmin[major])==i || floor(e->coordmax[major])==i || ceil(e->coordmax[major])==i) &&
856 (ceil(n->coordmin[major])==i || floor(n->coordmin[major])==i || floor(n->coordmax[major])==i || ceil(n->coordmax[major])==i) ) {
857 if (
858 (n==e->splinenext && n->tmin==e->tmax &&
859 n->tcur<n->tmin+.2 && e->tcur>e->tmax-.2 ) ||
860 (n->splinenext==e && n->tmax==e->tmin &&
861 n->tcur>n->tmax-.2 && e->tcur<e->tmin+.2 ) )
862 return( n->up!=e->up );
863 /* can be separated by a horizontal/vertical line in the other direction */
864 if ( n->tmax==1 && e->tmin==0 && n->tcur>.8 && e->tcur<.2) {
865 t = n;
866 while ( (t = t->splinenext)!=e ) {
867 if ( t==NULL || t==n ||
868 (major && !t->hor) || ( !major && !t->vert ))
869 return( false );
871 return( n->up!=e->up );
872 } else if ( n->tmin==0 && e->tmax==1 && n->tcur<.2 && e->tcur>.8) {
873 t = e;
874 while ( (t = t->splinenext)!=n ) {
875 if ( t==NULL || t==e ||
876 (major && !t->hor) || ( !major && !t->vert ))
877 return( false );
879 return( n->up!=e->up );
882 return( false );
886 real HIlen( StemInfo *stems) {
887 HintInstance *hi;
888 real len = 0;
890 for ( hi=stems->where; hi!=NULL; hi = hi->next )
891 len += hi->end-hi->begin;
892 return( len );
895 real HIoverlap( HintInstance *mhi, HintInstance *thi) {
896 HintInstance *hi;
897 real len = 0;
898 real s, e;
900 for ( ; mhi!=NULL; mhi = mhi->next ) {
901 for ( hi = thi; hi!=NULL && hi->begin<=mhi->end; hi = hi->next ) {
902 if ( hi->end<mhi->begin ) {
903 thi = hi;
904 continue;
906 s = hi->begin<mhi->begin?mhi->begin:hi->begin;
907 e = hi->end>mhi->end?mhi->end:hi->end;
908 if ( e<s )
909 continue; /* Shouldn't happen */
910 len += e-s;
913 return( len );
917 int StemListAnyConflicts(StemInfo *stems) {
918 StemInfo *s;
919 int any= false;
920 double end;
922 for ( s=stems; s!=NULL ; s=s->next )
923 s->hasconflicts = false;
924 while ( stems!=NULL ) {
925 end = stems->width<0 ? stems->start : stems->start+stems->width;
926 for ( s=stems->next; s!=NULL && (s->width>0 ? s->start : s->start+s->width)<end; s=s->next ) {
927 stems->hasconflicts = true;
928 s->hasconflicts = true;
929 any = true;
931 stems = stems->next;
933 return( any );
936 static HintInstance *SCGuessHintPoints(SplineChar *sc, int layer, StemInfo *stem,int major, int off) {
937 SplinePoint *starts[20], *ends[20];
938 int spt=0, ept=0;
939 SplinePointList *spl;
940 SplinePoint *sp, *np;
941 int sm, wm, i, j, val;
942 real coord;
943 HintInstance *head, *test, *cur, *prev;
945 for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
946 for ( sp=spl->first; ; sp = np ) {
947 coord = (major?sp->me.x:sp->me.y);
948 sm = coord>=stem->start-off && coord<=stem->start+off;
949 wm = coord>=stem->start+stem->width-off && coord<=stem->start+stem->width+off;
950 if ( sm && spt<20 )
951 starts[spt++] = sp;
952 if ( wm && ept<20 )
953 ends[ept++] = sp;
954 if ( sp->next==NULL )
955 break;
956 np = sp->next->to;
957 if ( np==spl->first )
958 break;
962 head = NULL;
963 for ( i=0; i<spt; ++i ) {
964 val = 0x80000000;
965 for ( j=0; j<ept; ++j ) {
966 if ( major && starts[i]->me.y>=ends[j]->me.y-1 && starts[i]->me.y<=ends[j]->me.y+1 ) {
967 val = starts[i]->me.y;
968 break;
969 } else if ( !major && starts[i]->me.x>=ends[j]->me.x-1 && starts[i]->me.x<=ends[j]->me.x+1 ) {
970 val = starts[i]->me.x;
971 break;
974 if ( (unsigned)val!=0x80000000 ) {
975 for ( prev=NULL, test=head; test!=NULL && val>test->begin; prev=test, test=test->next );
976 if ( test==NULL || val!=test->begin ) {
977 cur = chunkalloc(sizeof(HintInstance));
978 cur->begin = cur->end = val;
979 cur->next = test;
980 if ( prev==NULL ) head = cur;
981 else prev->next = cur;
985 return( head );
988 static void SCGuessHintInstancesLight(SplineChar *sc, int layer, StemInfo *stem,int major) {
989 SplinePointList *spl;
990 SplinePoint *sp, *np;
991 int sm, wm, off;
992 real ob, oe;
993 HintInstance *s=NULL, *w=NULL, *cur, *p, *t, *n, *w2;
994 /* We've got a hint (from somewhere, old data, reading in a font, user specified etc.) */
995 /* but we don't have HintInstance info. So see if we can find those data */
996 /* Will get confused by stems with holes in them (for example if you make */
997 /* a hint from one side of an H to the other, it will get the whole thing */
998 /* not just the cross stem) */
1000 for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
1001 for ( sp=spl->first; ; sp = np ) {
1002 sm = (major?sp->me.x:sp->me.y)==stem->start;
1003 wm = (major?sp->me.x:sp->me.y)==stem->start+stem->width;
1004 if ( sp->next==NULL )
1005 break;
1006 np = sp->next->to;
1007 if ( sm || wm ) {
1008 if ( !major ) {
1009 if ( np->me.y==sp->me.y ) {
1010 ob = sp->me.x; oe = np->me.x;
1011 } else if ( sp->nextcp.y==sp->me.y ) {
1012 ob = sp->me.x; oe = (sp->me.x+sp->nextcp.x)/2;
1013 if ( sp->prevcp.y==sp->me.y )
1014 ob = (sp->prevcp.x+sp->me.x)/2;
1015 } else if ( sp->prevcp.y==sp->me.y ) {
1016 ob = sp->me.x; oe = (sp->prevcp.x+sp->me.x)/2;
1017 } else
1018 sm = wm = false;
1019 } else {
1020 if ( np->me.x==sp->me.x ) {
1021 ob = sp->me.y; oe = np->me.y;
1022 } else if ( sp->nextcp.x==sp->me.x ) {
1023 ob = sp->me.y; oe = (sp->nextcp.y+sp->me.y)/2;
1024 if ( sp->prevcp.x==sp->me.x )
1025 ob = (sp->prevcp.y+sp->me.y)/2;
1026 } else if ( sp->prevcp.x==sp->me.x ) {
1027 ob = sp->me.y; oe = (sp->prevcp.y+sp->me.y)/2;
1028 } else
1029 sm = wm = false;
1032 if ( sm || wm ) {
1033 cur = chunkalloc(sizeof(HintInstance));
1034 if ( ob>oe ) { real temp=ob; ob=oe; oe=temp;}
1035 cur->begin = ob;
1036 cur->end = oe;
1037 if ( sm ) {
1038 if ( s==NULL || s->begin>cur->begin ) {
1039 cur->next = s;
1040 s = cur;
1041 } else {
1042 p = s;
1043 for ( t=s->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next );
1044 p->next = cur; cur->next = t;
1046 } else {
1047 if ( w==NULL || w->begin>cur->begin ) {
1048 cur->next = w;
1049 w = cur;
1050 } else {
1051 p = w;
1052 for ( t=w->next; t!=NULL && t->begin<cur->begin; p=t, t=t->next );
1053 p->next = cur; cur->next = t;
1057 if ( np==spl->first )
1058 break;
1062 /* Now we know what the right side of the stem looks like, and we know */
1063 /* what the left side looks like. They may not look the same (H for example) */
1064 /* Figure out the set where both are active */
1065 /* Unless it's a ghost hint */
1066 if ( stem->width==20 && s==NULL && w!=NULL ) {
1067 s = w;
1068 w = NULL;
1069 } else if ( stem->width==21 && s!=NULL && w==NULL) {
1070 /* Just use s */;
1071 } else for ( p=NULL, t=s; t!=NULL; t=n ) {
1072 n = t->next;
1073 for ( w2=w; w2!=NULL && w2->begin<t->end ; w2=w2->next ) {
1074 if ( w2->end<=t->begin )
1075 continue;
1076 if ( w2->begin<=t->begin && w2->end>=t->end ) {
1077 /* Perfect match */
1078 break;
1080 if ( w2->begin>=t->begin )
1081 t->begin = w2->begin;
1082 if ( w2->end<=t->end ) {
1083 cur = chunkalloc(sizeof(HintInstance));
1084 cur->begin = w2->end;
1085 cur->end = t->end;
1086 cur->next = n;
1087 t->next = cur;
1088 n = cur;
1089 t->end = w2->end;
1091 break;
1093 if ( w2!=NULL && w2->begin>=t->end )
1094 w2 = NULL;
1095 if ( w2==NULL && w!=NULL ) {
1096 HintInstance *best = NULL;
1097 double best_off=1e10, off;
1098 for ( w2=w; w2!=NULL ; w2=w2->next ) {
1099 if ( w2->end<=t->begin )
1100 off = t->begin - w2->end;
1101 else
1102 off = w2->begin - t->end;
1103 if ( best==NULL && off<best_off ) {
1104 best = w2;
1105 best_off = off;
1108 if ( best!=NULL && best_off<stem->width ) {
1109 w2 = best;
1110 if( w2->begin<t->begin )
1111 t->begin = w2->begin;
1112 if ( w2->end>t->end )
1113 t->end = w2->end;
1116 if ( w2==NULL ) {
1117 /* No match for t (or if there were it wasn't complete) get rid */
1118 /* of what's left of t */
1119 chunkfree(t,sizeof(*t));
1120 if ( p==NULL )
1121 s = n;
1122 else
1123 p->next = n;
1124 } else
1125 p = t;
1127 while ( w!=NULL ) {
1128 n = w->next;
1129 chunkfree(w,sizeof(*w));
1130 w=n;
1133 /* If we couldn't find anything, then see if there are two points which */
1134 /* have the same x or y value and whose other coordinates match those of */
1135 /* the hint */
1136 /* Surprisingly many fonts have hints which don't accurately match the */
1137 /* points. Perhaps BlueFuzz (default 1) applies here too */
1138 for ( off=0; off<1 && s==NULL; ++off )
1139 s = SCGuessHintPoints(sc,layer, stem,major,off);
1141 stem->where = s;
1144 void SCGuessHHintInstancesList(SplineChar *sc,int layer) {
1145 StemInfo *h;
1146 for ( h= sc->hstem; h!=NULL; h=h->next ) {
1147 if ( h->where==NULL ) {
1148 SCGuessHintInstancesLight( sc,layer,h,false );
1153 void SCGuessVHintInstancesList(SplineChar *sc,int layer) {
1154 StemInfo *h;
1155 for ( h= sc->vstem; h!=NULL; h=h->next ) {
1156 if ( h->where==NULL ) {
1157 SCGuessHintInstancesLight( sc,layer,h,true );
1163 static void _SCClearHintMasks(SplineChar *sc,int layer, int counterstoo) {
1164 SplineSet *spl;
1165 SplinePoint *sp;
1166 RefChar *ref;
1168 if ( counterstoo ) {
1169 free(sc->countermasks);
1170 sc->countermasks = NULL; sc->countermask_cnt = 0;
1173 for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
1174 for ( sp = spl->first ; ; ) {
1175 chunkfree(sp->hintmask,sizeof(HintMask));
1176 sp->hintmask = NULL;
1177 if ( sp->next==NULL )
1178 break;
1179 sp = sp->next->to;
1180 if ( sp==spl->first )
1181 break;
1185 for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
1186 for ( spl = ref->layers[0].splines; spl!=NULL; spl=spl->next ) {
1187 for ( sp = spl->first ; ; ) {
1188 chunkfree(sp->hintmask,sizeof(HintMask));
1189 sp->hintmask = NULL;
1190 if ( sp->next==NULL )
1191 break;
1192 sp = sp->next->to;
1193 if ( sp==spl->first )
1194 break;
1201 static void SCFigureSimpleCounterMasks(SplineChar *sc) {
1202 SplineChar *scs[MmMax];
1203 int hadh3, hadv3, i, vbase;
1204 HintMask mask;
1205 StemInfo *h;
1207 if ( sc->countermask_cnt!=0 )
1208 return;
1210 scs[0] = sc;
1211 hadh3 = CvtPsStem3(NULL,scs,1,true,false);
1212 hadv3 = CvtPsStem3(NULL,scs,1,false,false);
1213 if ( hadh3 || hadv3 ) {
1214 memset(mask,0,sizeof(mask));
1215 if ( hadh3 ) mask[0] = 0x80|0x40|0x20;
1216 if ( hadv3 ) {
1217 for ( h=sc->hstem, vbase=0; h!=NULL; h=h->next, ++vbase );
1218 for ( i=0; i<3 ; ++i ) {
1219 int j = i+vbase;
1220 mask[j>>3] |= (0x80>>(j&7));
1223 sc->countermask_cnt = 1;
1224 sc->countermasks = galloc(sizeof(HintMask));
1225 memcpy(sc->countermasks[0],mask,sizeof(HintMask));
1226 return;
1230 void SCClearHintMasks(SplineChar *sc,int layer,int counterstoo) {
1231 MMSet *mm = sc->parent->mm;
1232 int i;
1234 if ( mm==NULL )
1235 _SCClearHintMasks(sc,layer,counterstoo);
1236 else {
1237 for ( i=0; i<mm->instance_count; ++i ) {
1238 if ( sc->orig_pos<mm->instances[i]->glyphcnt )
1239 _SCClearHintMasks(mm->instances[i]->glyphs[sc->orig_pos],layer,counterstoo);
1241 if ( sc->orig_pos<mm->normal->glyphcnt )
1242 _SCClearHintMasks(mm->normal->glyphs[sc->orig_pos],layer,counterstoo);
1246 static StemInfo *OnHHint(SplinePoint *sp, StemInfo *s) {
1247 StemInfo *possible=NULL;
1248 HintInstance *hi;
1250 if ( sp==NULL )
1251 return( NULL );
1253 for ( ; s!=NULL; s=s->next ) {
1254 if ( sp->me.y<s->start )
1255 return( possible );
1256 if ( s->start==sp->me.y || s->start+s->width==sp->me.y ) {
1257 if ( !s->hasconflicts )
1258 return( s );
1259 for ( hi=s->where; hi!=NULL; hi=hi->next ) {
1260 if ( hi->begin<=sp->me.x && hi->end>=sp->me.x )
1261 return( s );
1263 if ( !s->used )
1264 possible = s;
1267 return( possible );
1270 static StemInfo *OnVHint(SplinePoint *sp, StemInfo *s) {
1271 StemInfo *possible=NULL;
1272 HintInstance *hi;
1274 if ( sp==NULL )
1275 return( NULL );
1277 for ( ; s!=NULL; s=s->next ) {
1278 if ( sp->me.x<s->start )
1279 return( possible );
1280 if ( s->start==sp->me.x || s->start+s->width==sp->me.x ) {
1281 if ( !s->hasconflicts )
1282 return( s );
1283 for ( hi=s->where; hi!=NULL; hi=hi->next ) {
1284 if ( hi->begin<=sp->me.y && hi->end>=sp->me.y )
1285 return( s );
1287 if ( !s->used )
1288 possible = s;
1291 return( possible );
1294 /* Does h have a conflict with any of the stems in the list which have bits */
1295 /* set in the mask */
1296 static int ConflictsWithMask(StemInfo *stems, HintMask mask,StemInfo *h) {
1297 while ( stems!=NULL && stems->start<h->start+h->width ) {
1298 if ( stems->start+stems->width>=h->start && stems!=h ) {
1299 if ( stems->hintnumber!=-1 &&
1300 (mask[stems->hintnumber>>3]&(0x80>>(stems->hintnumber&7))) )
1301 return( true );
1303 stems = stems->next;
1305 return( false );
1308 /* All instances of a MM set must have the same hint mask at all points */
1309 static void FigureHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count,
1310 HintMask mask) {
1311 StemInfo *s;
1312 int i;
1313 SplinePoint *sp;
1315 memset(mask,'\0',sizeof(HintMask));
1317 /* Install all hints that are always active */
1318 i=0; {
1319 SplineChar *sc = scs[i];
1321 if ( sc==NULL )
1322 return;
1324 for ( s=sc->hstem; s!=NULL; s=s->next )
1325 if ( s->hintnumber!=-1 && !s->hasconflicts )
1326 mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
1327 for ( s=sc->vstem; s!=NULL; s=s->next )
1328 if ( s->hintnumber!=-1 && !s->hasconflicts )
1329 mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
1331 if ( sc->hconflicts ) {
1332 for ( sp=to[i]; sp!=NULL; ) {
1333 s = OnHHint(sp,sc->hstem);
1334 if ( s!=NULL && s->hintnumber!=-1 ) {
1335 if ( ConflictsWithMask(scs[i]->hstem,mask,s))
1336 break;
1337 mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
1339 if ( sp->next==NULL )
1340 break;
1341 sp = sp->next->to;
1342 if ( to[i]==sp )
1343 break;
1346 if ( sc->vconflicts ) {
1347 for ( sp=to[i]; sp!=NULL; ) {
1348 s = OnVHint(sp,sc->vstem);
1349 if ( s!=NULL && s->hintnumber!=-1 ) {
1350 if ( ConflictsWithMask(scs[i]->vstem,mask,s))
1351 break;
1352 mask[s->hintnumber>>3] |= (0x80>>(s->hintnumber&7));
1354 if ( sp->next==NULL )
1355 break;
1356 sp = sp->next->to;
1357 if ( to[i]==sp )
1358 break;
1362 for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
1363 chunkfree(to[i]->hintmask,sizeof(HintMask));
1364 to[i]->hintmask = chunkalloc(sizeof(HintMask));
1365 memcpy(to[i]->hintmask,mask,sizeof(HintMask));
1369 static int TestHintMask(SplineChar *scs[MmMax], SplinePoint *to[MmMax], int instance_count,
1370 HintMask mask) {
1371 StemInfo *h=NULL, *v=NULL;
1372 int i;
1374 for ( i=0; i<instance_count; ++i ) {
1375 SplineChar *sc = scs[i];
1377 if ( sc==NULL || (!sc->hconflicts && !sc->vconflicts ))
1378 continue;
1380 /* Does this point lie on any hints? */
1381 if ( scs[i]->hconflicts )
1382 h = OnHHint(to[i],sc->hstem);
1383 if ( scs[i]->vconflicts )
1384 v = OnVHint(to[i],sc->vstem);
1386 /* Need to set this hint */
1387 if ( (h!=NULL && h->hintnumber!=-1 && (mask[h->hintnumber>>3]&(0x80>>(h->hintnumber&7)))==0 ) ||
1388 (v!=NULL && v->hintnumber!=-1 && (mask[v->hintnumber>>3]&(0x80>>(v->hintnumber&7)))==0 ))
1389 break;
1391 if ( i==instance_count ) /* All hint masks were ok */
1392 return( false );
1394 FigureHintMask(scs,to,instance_count,mask);
1395 return( true );
1398 static void UnnumberHints(SplineChar *sc) {
1399 StemInfo *h;
1401 for ( h=sc->hstem; h!=NULL; h=h->next )
1402 h->hintnumber = -1;
1403 for ( h=sc->vstem; h!=NULL; h=h->next )
1404 h->hintnumber = -1;
1407 static int NumberHints(SplineChar *sc) {
1408 StemInfo *h;
1409 int hcnt=0;
1411 for ( h=sc->hstem; h!=NULL; h=h->next )
1412 h->hintnumber = hcnt>=HntMax ? -1 : hcnt++;
1413 for ( h=sc->vstem; h!=NULL; h=h->next )
1414 h->hintnumber = hcnt>=HntMax ? -1 : hcnt++;
1415 return( hcnt );
1418 static void UntickHints(SplineChar *sc) {
1419 StemInfo *h;
1421 for ( h=sc->hstem; h!=NULL; h=h->next )
1422 h->used = false;
1423 for ( h=sc->vstem; h!=NULL; h=h->next )
1424 h->used = false;
1427 struct coords {
1428 real coords[MmMax];
1429 struct coords *next;
1432 typedef struct mmh {
1433 StemInfo *hints[MmMax], *map[MmMax];
1434 struct coords *where;
1435 struct mmh *next;
1436 } MMH;
1438 static void AddCoord(MMH *mmh,SplinePoint *sps[MmMax],int instance_count, int ish) {
1439 struct coords *coords;
1440 int i;
1442 coords = chunkalloc(sizeof(struct coords));
1443 coords->next = mmh->where;
1444 mmh->where = coords;
1445 if ( ish )
1446 for ( i=0; i<instance_count; ++i )
1447 coords->coords[i] = sps[i]->me.x;
1448 else
1449 for ( i=0; i<instance_count; ++i )
1450 coords->coords[i] = sps[i]->me.y;
1453 static MMH *AddHintSet(MMH *hints,StemInfo *h[MmMax], int instance_count,
1454 SplinePoint *sps[MmMax], int ish) {
1455 int i, cnt, bestc;
1456 MMH *test, *best;
1458 for ( i=0; i<instance_count; ++i )
1459 if ( h[i]==NULL )
1460 return( hints );
1462 best = NULL; bestc = 0;
1463 for ( test=hints; test!=NULL; test=test->next ) {
1464 cnt = 0;
1465 for ( i=0; i<instance_count; ++i )
1466 if ( test->hints[i]==h[i] )
1467 ++cnt;
1468 if ( cnt==instance_count ) {
1469 AddCoord(test,sps,instance_count,ish);
1470 return( hints );
1472 if ( cnt>bestc ) {
1473 bestc = cnt;
1474 best = test;
1477 test = chunkalloc(sizeof(MMH));
1478 test->next = hints;
1479 AddCoord(test,sps,instance_count,ish);
1480 for ( i=0; i<instance_count; ++i )
1481 test->hints[i]=h[i];
1482 if ( bestc!=0 ) {
1483 for ( i=0; i<instance_count; ++i ) {
1484 if ( best->hints[i]==h[i] ) {
1485 h[i]->hasconflicts = true;
1486 test->map[i] = chunkalloc(sizeof(StemInfo));
1487 *test->map[i] = *h[i];
1488 test->map[i]->where = NULL;
1489 test->map[i]->used = true;
1490 h[i]->next = test->map[i];
1491 } else
1492 test->map[i] = h[i];
1494 } else {
1495 for ( i=0; i<instance_count; ++i )
1496 test->map[i]=h[i];
1498 return( test );
1501 static int CompareMMH(MMH *mmh1,MMH *mmh2, int instance_count) {
1502 int i;
1504 if ( mmh1->map[0]==NULL )
1505 return( 1 );
1506 if ( mmh2->map[0]==NULL )
1507 return( -1 );
1509 for ( i=0; i<instance_count; ++i ) {
1510 if ( mmh1->map[i]->start!=mmh2->map[i]->start ) {
1511 if ( mmh1->map[i]->start > mmh2->map[i]->start )
1512 return( 1 );
1513 else
1514 return( -1 );
1517 for ( i=0; i<instance_count; ++i ) {
1518 if ( mmh1->map[i]->width!=mmh2->map[i]->width ) {
1519 if ( mmh1->map[i]->width > mmh2->map[i]->width )
1520 return( 1 );
1521 else
1522 return( -1 );
1525 return( 0 );
1528 static MMH *SortMMH(MMH *head,int instance_count) {
1529 MMH *mmh, *p, *smallest, *psmallest, *test, *ptest;
1531 for ( mmh = head, p=NULL; mmh!=NULL ; ) {
1532 smallest = mmh; psmallest = p;
1533 ptest = mmh; test = mmh->next;
1534 while ( test!=NULL ) {
1535 if ( CompareMMH(test,smallest,instance_count)<0 ) {
1536 smallest = test;
1537 psmallest = ptest;
1539 ptest = test;
1540 test = test->next;
1542 if ( smallest!=mmh ) {
1543 if ( p==NULL )
1544 head = smallest;
1545 else
1546 p->next = smallest;
1547 if ( mmh->next==smallest ) {
1548 mmh->next = smallest->next;
1549 smallest->next = mmh;
1550 } else {
1551 test = mmh->next;
1552 mmh->next = smallest->next;
1553 smallest->next = test;
1554 psmallest->next = mmh;
1557 p = smallest;
1558 mmh = smallest->next;
1560 return( head );
1563 static int NumberMMH(MMH *mmh,int hstart,int instance_count) {
1564 int i;
1565 HintInstance *hi, *n;
1566 struct coords *coords;
1568 while ( mmh!=NULL ) {
1569 for ( i=0; i<instance_count; ++i ) {
1570 StemInfo *h = mmh->map[i];
1571 if ( h==NULL )
1572 continue;
1574 h->hintnumber = hstart;
1576 for ( hi=h->where; hi!=NULL; hi=n ) {
1577 n = hi->next;
1578 chunkfree(hi,sizeof(HintInstance));
1580 h->where = NULL;
1581 for ( coords=mmh->where; coords!=NULL; coords = coords->next ) {
1582 hi = chunkalloc(sizeof(HintInstance));
1583 hi->next = h->where;
1584 h->where = hi;
1585 hi->begin = coords->coords[i]-1;
1586 hi->end = coords->coords[i]+1;
1589 if ( mmh->map[0]!=NULL ) ++hstart;
1590 mmh = mmh->next;
1592 return( hstart );
1595 static void SortMMH2(SplineChar *scs[MmMax],MMH *mmh,int instance_count,int ish) {
1596 int i;
1597 StemInfo *h, *n;
1598 MMH *m;
1600 for ( i=0; i<instance_count; ++i ) {
1601 for ( h= ish ? scs[i]->hstem : scs[i]->vstem; h!=NULL; h=n ) {
1602 n = h->next;
1603 if ( h->hintnumber==-1 )
1604 StemInfoFree(h);
1606 n = NULL;
1607 for ( m = mmh ; m!=NULL; m=m->next ) {
1608 h = m->map[i];
1609 if ( n!=NULL )
1610 n->next = h;
1611 else if ( ish )
1612 scs[i]->hstem = h;
1613 else
1614 scs[i]->vstem = h;
1615 n = h;
1617 if ( n!=NULL )
1618 n->next = NULL;
1619 else if ( ish )
1620 scs[i]->hstem = NULL;
1621 else
1622 scs[i]->vstem = NULL;
1626 static void MMHFreeList(MMH *mmh) {
1627 MMH *mn;
1628 struct coords *c, *n;
1630 for ( ; mmh!=NULL; mmh = mn ) {
1631 mn = mmh->next;
1632 for ( c=mmh->where; c!=NULL; c=n ) {
1633 n = c->next;
1634 chunkfree(c,sizeof(struct coords));
1636 chunkfree(mmh,sizeof(struct coords));
1640 static void SplResolveSplitHints(SplineChar *scs[MmMax], SplineSet *spl[MmMax],
1641 int instance_count, MMH **hs, MMH **vs) {
1642 SplinePoint *to[MmMax];
1643 StemInfo *h[MmMax], *v[MmMax];
1644 int i, anymore;
1646 forever {
1647 for ( i=0; i<instance_count; ++i ) {
1648 if ( spl[i]!=NULL )
1649 to[i] = spl[i]->first;
1650 else
1651 to[i] = NULL;
1653 forever {
1654 for ( i=0; i<instance_count; ++i ) {
1655 h[i] = OnHHint(to[i],scs[i]->hstem);
1656 v[i] = OnVHint(to[i],scs[i]->vstem);
1658 *hs = AddHintSet(*hs,h,instance_count,to,true);
1659 *vs = AddHintSet(*vs,v,instance_count,to,false);
1660 anymore = false;
1661 for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
1662 if ( to[i]->next==NULL ) to[i] = NULL;
1663 else {
1664 to[i] = to[i]->next->to;
1665 if ( to[i]==spl[i]->first ) to[i] = NULL;
1667 if ( to[i]!=NULL ) anymore = true;
1669 if ( !anymore )
1670 break;
1672 anymore = false;
1673 for ( i=0; i<instance_count; ++i ) {
1674 if ( spl[i]!=NULL )
1675 spl[i] = spl[i]->next;
1676 if ( spl[i]!=NULL ) anymore = true;
1678 if ( !anymore )
1679 break;
1683 static void ResolveSplitHints(SplineChar *scs[16],int layer,int instance_count) {
1684 /* It is possible for a single hint in one mm instance to split into two */
1685 /* in a different MM set. For example, we have two stems which happen */
1686 /* to line up in one instance but which do not in another instance. */
1687 /* It is even possible that there could be no instance with any conflicts */
1688 /* but some of the intermediate forms might conflict. */
1689 /* We can't deal (nor can postscript) with the case where hints change order*/
1690 SplinePointList *spl[MmMax];
1691 RefChar *ref[MmMax];
1692 int i, hcnt, hmax=0, anymore;
1693 MMH *hs=NULL, *vs=NULL;
1695 for ( i=0; i<instance_count; ++i ) {
1696 hcnt = NumberHints(scs[i]);
1697 UntickHints(scs[i]);
1698 if ( hcnt>hmax ) hmax = hcnt;
1699 spl[i] = scs[i]->layers[layer].splines;
1701 if ( hmax==0 )
1702 return;
1704 SplResolveSplitHints(scs,spl,instance_count,&hs,&vs);
1705 anymore = false;
1706 for ( i=0; i<instance_count; ++i ) {
1707 ref[i] = scs[i]->layers[layer].refs;
1708 if ( ref[i]!=NULL ) anymore = true;
1710 while ( anymore ) {
1711 for ( i=0; i<instance_count; ++i )
1712 spl[i] = ( ref[i]!=NULL ) ? ref[i]->layers[0].splines : NULL;
1713 SplResolveSplitHints(scs,spl,instance_count,&hs,&vs);
1714 anymore = false;
1715 for ( i=0; i<instance_count; ++i ) {
1716 if ( ref[i]!=NULL ) {
1717 ref[i] = ref[i]->next;
1718 if ( ref[i]!=NULL ) anymore = true;
1723 for ( i=0; i<instance_count; ++i )
1724 UnnumberHints(scs[i]);
1725 hs = SortMMH(hs,instance_count);
1726 vs = SortMMH(vs,instance_count);
1727 hcnt = NumberMMH(hs,0,instance_count);
1728 hcnt = NumberMMH(vs,hcnt,instance_count);
1729 SortMMH2(scs,hs,instance_count,true);
1730 SortMMH2(scs,vs,instance_count,false);
1731 MMHFreeList(hs);
1732 MMHFreeList(vs);
1735 static int SplFigureHintMasks(SplineChar *scs[MmMax], SplineSet *spl[MmMax],
1736 int instance_count, HintMask mask, int inited) {
1737 SplinePoint *to[MmMax];
1738 int i, anymore;
1740 anymore = false;
1741 for ( i=0; i<instance_count; ++i ) {
1742 if ( spl[i]!=NULL ) {
1743 SplineSetReverse(spl[i]);
1744 to[i] = spl[i]->first;
1745 anymore = true;
1746 } else
1747 to[i] = NULL;
1750 /* Assign the initial hint mask */
1751 if ( anymore && !inited ) {
1752 FigureHintMask(scs,to,instance_count,mask);
1753 inited = true;
1756 forever {
1757 for ( i=0; i<instance_count; ++i ) {
1758 if ( spl[i]!=NULL )
1759 to[i] = spl[i]->first;
1760 else
1761 to[i] = NULL;
1763 forever {
1764 TestHintMask(scs,to,instance_count,mask);
1765 anymore = false;
1766 for ( i=0; i<instance_count; ++i ) if ( to[i]!=NULL ) {
1767 if ( to[i]->next==NULL ) to[i] = NULL;
1768 else {
1769 to[i] = to[i]->next->to;
1770 if ( to[i]==spl[i]->first ) to[i] = NULL;
1772 if ( to[i]!=NULL ) anymore = true;
1774 if ( !anymore )
1775 break;
1777 anymore = false;
1778 for ( i=0; i<instance_count; ++i ) {
1779 if ( spl[i]!=NULL ) {
1780 SplineSetReverse(spl[i]);
1781 spl[i] = spl[i]->next;
1783 if ( spl[i]!=NULL ) {
1784 anymore = true;
1785 SplineSetReverse(spl[i]);
1788 if ( !anymore )
1789 break;
1791 return( inited );
1794 void SCFigureHintMasks(SplineChar *sc,int layer) {
1795 SplineChar *scs[MmMax];
1796 SplinePointList *spl[MmMax];
1797 RefChar *ref[MmMax];
1798 MMSet *mm = sc->parent->mm;
1799 int i, instance_count, conflicts, anymore, inited;
1800 HintMask mask;
1802 if ( mm==NULL ) {
1803 scs[0] = sc;
1804 instance_count = 1;
1805 SCClearHintMasks(sc,layer,false);
1806 } else {
1807 instance_count = mm->instance_count;
1808 for ( i=0; i<instance_count; ++i )
1809 if ( sc->orig_pos < mm->instances[i]->glyphcnt ) {
1810 scs[i] = mm->instances[i]->glyphs[sc->orig_pos];
1811 SCClearHintMasks(scs[i],layer,false);
1813 ResolveSplitHints(scs,layer,instance_count);
1815 conflicts = false;
1816 for ( i=0; i<instance_count; ++i ) {
1817 NumberHints(scs[i]);
1818 if ( scs[i]->hconflicts || scs[i]->vconflicts )
1819 conflicts = true;
1821 if ( !conflicts && instance_count==1 ) { /* All hints always active */
1822 SCFigureSimpleCounterMasks(sc);
1823 return; /* In an MM font we may still need to resolve things like different numbers of hints */
1826 for ( i=0; i<instance_count; ++i ) {
1827 spl[i] = scs[i]->layers[layer].splines;
1828 ref[i] = scs[i]->layers[layer].refs;
1830 inited = SplFigureHintMasks(scs,spl,instance_count,mask,false);
1831 forever {
1832 for ( i=0; i<instance_count; ++i ) {
1833 if ( ref[i]!=NULL )
1834 spl[i] = ref[i]->layers[0].splines;
1836 inited = SplFigureHintMasks(scs,spl,instance_count,mask,inited);
1837 anymore = false;
1838 for ( i=0; i<instance_count; ++i ) {
1839 if ( ref[i]!=NULL ) {
1840 ref[i] = ref[i]->next;
1841 if ( ref[i]!=NULL ) anymore = true;
1844 if ( !anymore )
1845 break;
1847 if ( instance_count==1 )
1848 SCFigureSimpleCounterMasks(sc);
1853 static void FigureStems( SplineFont *sf, real snaps[12], real cnts[12],
1854 int which ) {
1855 int i, j, k, cnt, smax=0, smin=2000;
1856 real stemwidths[2000];
1857 StemInfo *stems, *test;
1858 int len;
1859 HintInstance *hi;
1861 memset(stemwidths,'\0',sizeof(stemwidths));
1863 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
1864 stems = which?sf->glyphs[i]->hstem:sf->glyphs[i]->vstem;
1865 for ( test=stems; test!=NULL; test = test->next ) if ( !test->ghost ) {
1866 if ( (j=test->width)<0 ) j= -j;
1867 if ( j<2000 ) {
1868 len = 0;
1869 for ( hi=test->where; hi!=NULL; hi=hi->next )
1870 len += hi->end-hi->begin;
1871 if ( len==0 ) len = 100;
1872 stemwidths[j] += len;
1873 if ( smax<j ) smax=j;
1874 if ( smin>j ) smin=j;
1879 for ( i=smin, cnt=0; i<=smax; ++i ) {
1880 if ( stemwidths[i]!=0 )
1881 ++cnt;
1884 if ( cnt>12 ) {
1885 /* Merge width windows */
1886 int windsize=3, j;
1887 for ( i=smin; i<=smax; ++i ) if ( stemwidths[i]!=0 ) {
1888 if ( (j = i-windsize)<0 ) j=0;
1889 for ( ; j<smax && j<=i+windsize; ++j )
1890 if ( stemwidths[i]<stemwidths[j] )
1891 break;
1892 if ( j==smax || j>i+windsize ) {
1893 if ( (j = i-windsize)<0 ) j=0;
1894 for ( ; j<smax && j<=i+windsize; ++j ) if ( j!=i ) {
1895 stemwidths[i] += stemwidths[j];
1896 stemwidths[j] = 0;
1900 /* Merge adjacent widths */
1901 for ( i=smin; i<=smax; ++i ) {
1902 if ( stemwidths[i]!=0 && i<=smax-1 && stemwidths[i+1]!=0 ) {
1903 if ( stemwidths[i]>stemwidths[i+1] ) {
1904 stemwidths[i] += stemwidths[i+1];
1905 stemwidths[i+1] = 0;
1906 } else {
1907 if ( i<=smax-2 && stemwidths[i+2] && stemwidths[i+2]<stemwidths[i+1] ) {
1908 stemwidths[i+1] += stemwidths[i+2];
1909 stemwidths[i+2] = 0;
1911 stemwidths[i+1] += stemwidths[i];
1912 stemwidths[i] = 0;
1913 ++i;
1917 for ( i=smin, cnt=0; i<=smax; ++i ) {
1918 if ( stemwidths[i]!=0 )
1919 ++cnt;
1922 if ( cnt<=12 ) {
1923 for ( i=smin, cnt=0; i<=smax; ++i ) {
1924 if ( stemwidths[i]!=0 ) {
1925 snaps[cnt] = i;
1926 cnts[cnt++] = stemwidths[i];
1929 } else { real firstbiggest=0;
1930 for ( cnt = 0; cnt<12; ++cnt ) {
1931 int biggesti=0;
1932 real biggest=0;
1933 for ( i=smin; i<=smax; ++i ) {
1934 if ( stemwidths[i]>biggest ) { biggest = stemwidths[i]; biggesti=i; }
1936 /* array must be sorted */
1937 if ( biggest<firstbiggest/6 )
1938 break;
1939 for ( j=0; j<cnt; ++j )
1940 if ( snaps[j]>biggesti )
1941 break;
1942 for ( k=cnt-1; k>=j; --k ) {
1943 snaps[k+1] = snaps[k];
1944 cnts[k+1]=cnts[k];
1946 snaps[j] = biggesti;
1947 cnts[j] = biggest;
1948 stemwidths[biggesti] = 0;
1949 if ( firstbiggest==0 ) firstbiggest = biggest;
1952 for ( ; cnt<12; ++cnt ) {
1953 snaps[cnt] = 0;
1954 cnts[cnt] = 0;
1958 void FindHStems( SplineFont *sf, real snaps[12], real cnt[12]) {
1959 FigureStems(sf,snaps,cnt,1);
1962 void FindVStems( SplineFont *sf, real snaps[12], real cnt[12]) {
1963 FigureStems(sf,snaps,cnt,0);
1966 static int IsFlexSmooth(SplinePoint *sp) {
1967 BasePoint nvec, pvec;
1968 double proj_same, proj_normal;
1970 if ( sp->nonextcp || sp->noprevcp )
1971 return( false ); /* No continuity of slopes */
1973 nvec.x = sp->nextcp.x - sp->me.x; nvec.y = sp->nextcp.y - sp->me.y;
1974 pvec.x = sp->me.x - sp->prevcp.x; pvec.y = sp->me.y - sp->prevcp.y;
1976 /* Avoid cases where the slopes are 180 out of phase */
1977 if ( (proj_same = nvec.x*pvec.x + nvec.y*pvec.y)<=0 )
1978 return( false );
1979 if ( (proj_normal = nvec.x*pvec.y - nvec.y*pvec.x)<0 )
1980 proj_normal = -proj_normal;
1982 /* Something is smooth if the normal projection is 0. Let's allow for */
1983 /* some rounding errors */
1984 if ( proj_same >= 16*proj_normal )
1985 return( true );
1987 return( false );
1990 static int _SplineCharIsFlexible(SplineChar *sc, int layer, int blueshift) {
1991 /* Need two splines
1992 outer endpoints have same x (or y) values
1993 inner point must be less than 20 horizontal (v) units from the outer points
1994 inner point must also be less than BlueShift units (defaults to 7=>6)
1995 (can increase BlueShift up to 21)
1996 the inner point must be a local extremum
1997 the inner point's cps must be at the x (or y) value as the extremum
1998 (I think)
2000 /* We want long, nearly straight stems. If the end-points should not have
2001 continuous slopes, or if they do, they must be horizontal/vertical.
2002 This is an heuristic requirement, not part of Adobe's spec.
2004 SplineSet *spl;
2005 SplinePoint *sp, *np, *pp;
2006 int max=0, val;
2007 RefChar *r;
2009 if ( sc==NULL )
2010 return(false);
2012 for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
2013 if ( spl->first->prev==NULL ) {
2014 /* Mark everything on the open path as inflexible */
2015 sp=spl->first;
2016 while ( 1 ) {
2017 sp->flexx = sp->flexy = false;
2018 if ( sp->next==NULL )
2019 break;
2020 sp = sp->next->to;
2022 continue; /* Ignore open paths */
2024 sp=spl->first;
2025 do {
2026 if ( sp->next==NULL || sp->prev==NULL )
2027 break;
2028 np = sp->next->to;
2029 pp = sp->prev->from;
2030 if ( !pp->flexx && !pp->flexy ) {
2031 sp->flexy = sp->flexx = 0;
2032 val = 0;
2033 if ( RealNear(sp->nextcp.x,sp->me.x) &&
2034 RealNear(sp->prevcp.x,sp->me.x) &&
2035 RealNear(np->me.x,pp->me.x) &&
2036 !RealNear(np->me.x,sp->me.x) &&
2037 (!IsFlexSmooth(pp) || RealNear(pp->nextcp.x,pp->me.x)) &&
2038 (!IsFlexSmooth(np) || RealNear(np->prevcp.x,np->me.x)) &&
2039 np->me.x-sp->me.x < blueshift &&
2040 np->me.x-sp->me.x > -blueshift ) {
2041 if ( (np->me.x>sp->me.x &&
2042 np->prevcp.x<=np->me.x && np->prevcp.x>=sp->me.x &&
2043 pp->nextcp.x<=pp->me.x && pp->prevcp.x>=sp->me.x ) ||
2044 (np->me.x<sp->me.x &&
2045 np->prevcp.x>=np->me.x && np->prevcp.x<=sp->me.x &&
2046 pp->nextcp.x>=pp->me.x && pp->prevcp.x<=sp->me.x )) {
2047 sp->flexx = true;
2048 val = np->me.x-sp->me.x;
2051 if ( RealNear(sp->nextcp.y,sp->me.y) &&
2052 RealNear(sp->prevcp.y,sp->me.y) &&
2053 RealNear(np->me.y,pp->me.y) &&
2054 !RealNear(np->me.y,sp->me.y) &&
2055 (!IsFlexSmooth(pp) || RealNear(pp->nextcp.y,pp->me.y)) &&
2056 (!IsFlexSmooth(np) || RealNear(np->prevcp.y,np->me.y)) &&
2057 np->me.y-sp->me.y < blueshift &&
2058 np->me.y-sp->me.y > -blueshift ) {
2059 if ( (np->me.y>sp->me.y &&
2060 np->prevcp.y<=np->me.y && np->prevcp.y>=sp->me.y &&
2061 pp->nextcp.y<=pp->me.y && pp->nextcp.y>=sp->me.y ) ||
2062 (np->me.y<sp->me.y &&
2063 np->prevcp.y>=np->me.y && np->prevcp.y<=sp->me.y &&
2064 pp->nextcp.y>=pp->me.y && pp->nextcp.y<=sp->me.y )) {
2065 sp->flexy = true;
2066 val = np->me.y-sp->me.y;
2069 if ( val<0 ) val = -val;
2070 if ( val>max ) max = val;
2072 sp = np;
2073 } while ( sp!=spl->first );
2075 sc->layers[layer].anyflexes = max>0;
2076 if ( max==0 )
2077 for ( r = sc->layers[layer].refs; r!=NULL ; r=r->next )
2078 if ( r->sc->layers[layer].anyflexes ) {
2079 sc->layers[layer].anyflexes = true;
2080 break;
2082 return( max );
2087 static void SCUnflex(SplineChar *sc, int layer) {
2088 SplineSet *spl;
2089 SplinePoint *sp;
2091 for ( spl = sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
2092 /* Mark everything on the path as inflexible */
2093 sp=spl->first;
2094 while ( 1 ) {
2095 sp->flexx = sp->flexy = false;
2096 if ( sp->next==NULL )
2097 break;
2098 sp = sp->next->to;
2099 if ( sp==spl->first )
2100 break;
2103 sc->layers[layer].anyflexes = false;
2106 static void FlexDependents(SplineChar *sc,int layer) {
2107 struct splinecharlist *scl;
2109 sc->layers[layer].anyflexes = true;
2110 for ( scl = sc->dependents; scl!=NULL; scl=scl->next )
2111 FlexDependents(scl->sc,layer);
2114 int SplineFontIsFlexible(SplineFont *sf,int layer, int flags) {
2115 int i;
2116 int max=0, val;
2117 char *pt;
2118 int blueshift;
2119 /* if the return value is bigger than 6 and we don't have a BlueShift */
2120 /* then we must set BlueShift to ret+1 before saving private dictionary */
2121 /* If the first point in a spline set is flexible, then we must rotate */
2122 /* the splineset */
2124 if ( flags&(ps_flag_nohints|ps_flag_noflex)) {
2125 for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
2126 SCUnflex(sf->glyphs[i],layer);
2127 return( 0 );
2130 pt = PSDictHasEntry(sf->private,"BlueShift");
2131 blueshift = 21; /* maximum posible flex, not default */
2132 if ( pt!=NULL ) {
2133 blueshift = strtol(pt,NULL,10);
2134 if ( blueshift>21 ) blueshift = 21;
2135 } else if ( PSDictHasEntry(sf->private,"BlueValues")!=NULL )
2136 blueshift = 7; /* The BlueValues array may depend on BlueShift having its default value */
2138 for ( i=0; i<sf->glyphcnt; ++i )
2139 if ( sf->glyphs[i]!=NULL ) if ( sf->glyphs[i]!=NULL ) {
2140 val = _SplineCharIsFlexible(sf->glyphs[i],layer,blueshift);
2141 if ( val>max ) max = val;
2142 if ( sf->glyphs[i]->layers[layer].anyflexes )
2143 FlexDependents(sf->glyphs[i],layer);
2145 return( max );