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