1 /* Copyright (C) 2000-2008 by George Williams */
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "splinefont.h"
36 /* to create a type 1 font we must come up with the following entries for the
38 BlueValues -- an array of 2n entries where Blue[2i]<Blue[2i+1] max n=7, Blue[i]>0
39 OtherBlues -- (optional) OtherBlue[i]<0
40 (blue zones should be at least 3 units appart)
41 StdHW -- (o) array with one entry, standard hstem height
42 StdVW -- (o) ditto vstem width
43 StemSnapH -- (o) up to 12 numbers containing various hstem heights (includes StdHW), small widths first
44 StemSnapV -- (o) ditto, vstem
45 This file has routines to figure out at least some of these
47 Also other routines to guess at good per-character hints
50 static void AddBlue(real val
, real array
[5], int force
) {
52 if ( !force
&& (val
<array
[0]-array
[1] || val
>array
[0]+array
[1] ))
53 return; /* Outside of one sd */
54 if ( array
[3]==0 && array
[4]==0 )
55 array
[3] = array
[4] = val
;
56 else if ( val
>array
[4] )
58 else if ( val
<array
[3] )
62 static void MergeZones(real zone1
[5], real zone2
[5]) {
63 if ( zone1
[2]!=0 && zone2
[2]!=0 &&
64 ((zone1
[4]+3>zone2
[3] && zone1
[3]<=zone2
[3]) ||
65 (zone2
[4]+3>zone1
[3] && zone2
[3]<=zone1
[3]) )) {
66 if (( zone2
[0]<zone1
[0]-zone1
[1] || zone2
[0] >zone1
[0]+zone1
[1] ) &&
67 ( zone1
[0]<zone2
[0]-zone2
[1] || zone1
[0] >zone2
[0]+zone2
[1] ))
68 /* the means of the zones are too far appart, don't merge em */;
70 if ( zone1
[0]<zone2
[0] ) zone2
[0] = zone1
[0];
71 if ( zone1
[1]>zone2
[1] ) zone2
[1] = zone1
[1];
77 /* I can deal with latin, greek and cyrillic because the they all come from */
78 /* the same set of letter shapes and have all evolved together and have */
79 /* various common features (ascenders, descenders, lower case, etc.). Other */
80 /* scripts don't fit */
81 void FindBlues( SplineFont
*sf
, int layer
, real blues
[14], real otherblues
[10]) {
82 real caph
[5], xh
[5], ascenth
[5], digith
[5], descenth
[5], base
[5];
87 /* Go through once to get some idea of the average value so we can weed */
88 /* out undesireables */
89 caph
[0] = caph
[1] = caph
[2] = xh
[0] = xh
[1] = xh
[2] = 0;
90 ascenth
[0] = ascenth
[1] = ascenth
[2] = digith
[0] = digith
[1] = digith
[2] = 0;
91 descenth
[0] = descenth
[1] = descenth
[2] = base
[0] = base
[1] = base
[2] = 0;
92 otherdigits
[0] = otherdigits
[1] = otherdigits
[2] = 0;
93 for ( i
=0; i
<sf
->glyphcnt
; ++i
) {
94 if ( sf
->glyphs
[i
]!=NULL
&& sf
->glyphs
[i
]->layers
[layer
].splines
!=NULL
) {
95 int enc
= sf
->glyphs
[i
]->unicodeenc
;
99 if ( enc
<0x10000 && isalnum(enc
) &&
100 ((enc
>=32 && enc
<128 ) || enc
== 0xfe || enc
==0xf0 || enc
==0xdf ||
102 (enc
>=0x391 && enc
<=0x3f3 ) ||
103 (enc
>=0x400 && enc
<=0x4e9 ) )) {
104 /* no accented characters (or ligatures) */
106 if ( unicode_alternates
[enc
>>8]!=NULL
&&
107 (upt
=unicode_alternates
[enc
>>8][enc
&0xff])!=NULL
&&
111 SplineCharFindBounds(sf
->glyphs
[i
],&b
);
112 if ( b
.miny
==0 && b
.maxy
==0 )
114 if ( enc
=='g' || enc
=='j' || enc
=='p' || enc
=='q' || enc
=='y' ||
116 enc
==0x3c1 /* rho */ ||
117 enc
==0x3c6 /* phi */ ||
118 enc
==0x3c7 /* chi */ ||
119 enc
==0x3c8 /* psi */ ||
120 enc
==0x440 /* cyr er */ ||
121 enc
==0x443 /* cyr u */ ||
122 enc
==0x444 /* cyr ef */) {
123 descenth
[0] += b
.miny
;
124 descenth
[1] += b
.miny
*b
.miny
;
126 } else if ( enc
=='x' || enc
=='r' || enc
=='o' || enc
=='e' ||
127 enc
=='s' || enc
=='c' || enc
=='h' || enc
=='k' ||
128 enc
=='l' || enc
=='m' || enc
=='n' ||
129 enc
==0x3b5 /* epsilon */ ||
130 enc
==0x3b9 /* iota */ ||
131 enc
==0x3ba /* kappa */ ||
132 enc
==0x3bf /* omicron */ ||
133 enc
==0x3c3 /* sigma */ ||
134 enc
==0x3c5 /* upsilon */ ||
135 enc
==0x430 /* cyr a */ ||
136 enc
==0x432 /* cyr ve */ ||
137 enc
==0x433 /* cyr ge */ ||
138 enc
==0x435 /* cyr e */ ||
139 enc
==0x436 /* cyr zhe */ ||
140 enc
==0x438 /* cyr i */ ||
141 enc
==0x43a /* cyr ka */ ||
142 enc
==0x43d /* cyr en */ ||
143 enc
==0x43e /* cyr o */ ||
144 enc
==0x441 /* cyr es */ ||
145 enc
==0x445 /* cyr ha */ ||
146 enc
==0x447 /* cyr che */ ||
147 enc
==0x448 /* cyr sha */ ||
148 enc
==0x44f /* cyr ya */ ){
150 base
[1] += b
.miny
*b
.miny
;
153 /* careful of lowercase digits, 6 and 8 should be ascenders */
154 if ( enc
=='6' || enc
=='8' ) {
156 digith
[1] += b
.maxy
*b
.maxy
;
158 } else if ( enc
<0x10000 && isdigit(enc
) ) {
159 otherdigits
[0] += b
.maxy
;
160 otherdigits
[1] += b
.maxy
*b
.maxy
;
162 } else if ( enc
<0x10000 && isupper(enc
) && enc
!=0x462 && enc
!=0x490 ) {
164 caph
[1] += b
.maxy
*b
.maxy
;
166 } else if ( enc
=='b' || enc
=='d' || enc
=='f' || enc
=='h' || enc
=='k' ||
167 enc
== 'l' || enc
==0xf0 || enc
==0xfe || enc
== 0xdf ||
168 enc
== 0x3b2 || enc
==0x3b6 || enc
==0x3b8 || enc
==0x3bb ||
170 enc
== 0x431 /* cyr be */ /* || enc == 0x444 - ef may have varible height */) {
171 ascenth
[0] += b
.maxy
;
172 ascenth
[1] += b
.maxy
*b
.maxy
;
174 } else if ( enc
=='c' || enc
=='e' || enc
=='o' || enc
=='s' || enc
=='u' ||
175 enc
=='u' || enc
=='v' || enc
=='w' || enc
=='x' || enc
=='y' ||
177 enc
==0x3b5 /* epsilon */ ||
178 enc
==0x3b9 /* iota */ ||
179 enc
==0x3ba /* kappa */ ||
180 enc
==0x3bc /* mu */ ||
181 enc
==0x3bd /* nu */ ||
182 enc
==0x3bf /* omicron */ ||
183 enc
==0x3c0 /* pi */ ||
184 enc
==0x3c1 /* rho */ ||
185 enc
==0x3c5 /* upsilon */ ||
186 enc
==0x433 /* cyr ge */ ||
187 enc
==0x435 /* cyr e */ ||
188 enc
==0x436 /* cyr zhe */ ||
189 enc
==0x438 /* cyr i */ ||
190 enc
==0x43b /* cyr el */ ||
191 enc
==0x43d /* cyr en */ ||
192 enc
==0x43e /* cyr o */ ||
193 enc
==0x43f /* cyr pe */ ||
194 enc
==0x440 /* cyr er */ ||
195 enc
==0x441 /* cyr es */ ||
196 enc
==0x442 /* cyr te */ ||
197 enc
==0x443 /* cyr u */ ||
198 enc
==0x445 /* cyr ha */ ||
199 enc
==0x446 /* cyr tse */ ||
200 enc
==0x447 /* cyr che */ ||
201 enc
==0x448 /* cyr sha */ ||
202 enc
==0x449 /* cyr shcha */ ||
203 enc
==0x44a /* cyr hard sign */ ||
204 enc
==0x44b /* cyr yery */ ||
205 enc
==0x44c /* cyr soft sign */ ||
206 enc
==0x44d /* cyr reversed e */ ||
207 enc
==0x44f /* cyr ya */ ) {
209 xh
[1] += b
.maxy
*b
.maxy
;
214 if ( !ff_progress_next())
217 if ( otherdigits
[2]>0 && digith
[2]>0 ) {
218 if ( otherdigits
[0]/otherdigits
[2] >= .95*digith
[0]/digith
[2] ) {
219 /* all digits are about the same height, not lowercase */
220 digith
[0] += otherdigits
[0];
221 digith
[1] += otherdigits
[1];
222 digith
[2] += otherdigits
[2];
227 xh
[1] = sqrt((xh
[1]-xh
[0]*xh
[0]/xh
[2])/(xh
[2]-1));
230 if ( ascenth
[2]>1 ) {
231 ascenth
[1] = sqrt((ascenth
[1]-ascenth
[0]*ascenth
[0]/ascenth
[2])/(ascenth
[2]-1));
232 ascenth
[0] /= ascenth
[2];
235 caph
[1] = sqrt((caph
[1]-caph
[0]*caph
[0]/caph
[2])/(caph
[2]-1));
239 digith
[1] = sqrt((digith
[1]-digith
[0]*digith
[0]/digith
[2])/(digith
[2]-1));
240 digith
[0] /= digith
[2];
243 base
[1] = sqrt((base
[1]-base
[0]*base
[0]/base
[2])/(base
[2]-1));
246 if ( descenth
[2]>1 ) {
247 descenth
[1] = sqrt((descenth
[1]-descenth
[0]*descenth
[0]/descenth
[2])/(descenth
[2]-1));
248 descenth
[0] /= descenth
[2];
251 /* we'll accept values between +/- 1sd of the mean */
252 /* array[0] == mean, array[1] == sd, array[2] == cnt, array[3]=min, array[4]==max */
253 if ( base
[0]+base
[1]<0 ) base
[1] = -base
[0]; /* Make sure 0 is within the base bluezone */
254 caph
[3] = caph
[4] = 0;
256 ascenth
[3] = ascenth
[4] = 0;
257 digith
[3] = digith
[4] = 0;
258 descenth
[3] = descenth
[4] = 0;
259 base
[3] = base
[4] = 0;
260 for ( i
=0; i
<sf
->glyphcnt
; ++i
) if ( sf
->glyphs
[i
]!=NULL
) {
261 int enc
= sf
->glyphs
[i
]->unicodeenc
;
263 const unichar_t
*upt
;
265 if ( enc
<0x10000 && isalnum(enc
) &&
266 ((enc
>=32 && enc
<128 ) || enc
== 0xfe || enc
==0xf0 || enc
==0xdf ||
267 (enc
>=0x391 && enc
<=0x3f3 ) ||
268 (enc
>=0x400 && enc
<=0x4e9 ) )) {
269 /* no accented characters (or ligatures) */
271 if ( unicode_alternates
[enc
>>8]!=NULL
&&
272 (upt
=unicode_alternates
[enc
>>8][enc
&0xff])!=NULL
&&
276 SplineCharFindBounds(sf
->glyphs
[i
],&b
);
277 if ( b
.miny
==0 && b
.maxy
==0 )
279 if ( enc
=='g' || enc
=='j' || enc
=='p' || enc
=='q' || enc
=='y' ||
280 enc
==0xfe || enc
== 0x3c6 || enc
==0x3c8 ||
281 enc
==0x440 || enc
==0x443 || enc
==0x444) {
282 AddBlue(b
.miny
,descenth
,false);
284 /* O and o get forced into the baseline blue value even if they*/
285 /* are beyond 1 sd */
286 AddBlue(b
.miny
,base
,enc
=='O' || enc
=='o');
288 if ( enc
<0x10000 && isdigit(enc
)) {
289 AddBlue(b
.maxy
,digith
,false);
290 } else if ( enc
<0x10000 && isupper(enc
)) {
291 AddBlue(b
.maxy
,caph
,enc
=='O');
292 } else if ( enc
=='b' || enc
=='d' || enc
=='f' || enc
=='h' || enc
=='k' ||
293 enc
== 'l' || enc
=='t' || enc
==0xf0 || enc
==0xfe || enc
== 0xdf ||
294 enc
== 0x3b2 || enc
==0x3b6 || enc
==0x3b8 || enc
==0x3bb ||
297 AddBlue(b
.maxy
,ascenth
,false);
298 } else if ( enc
=='c' || enc
=='e' || enc
=='o' || enc
=='s' || enc
=='u' ||
299 enc
=='u' || enc
=='v' || enc
=='w' || enc
=='x' || enc
=='y' ||
301 enc
==0x3b5 /* epsilon */ ||
302 enc
==0x3b9 /* iota */ ||
303 enc
==0x3ba /* kappa */ ||
304 enc
==0x3bc /* mu */ ||
305 enc
==0x3bd /* nu */ ||
306 enc
==0x3bf /* omicron */ ||
307 enc
==0x3c0 /* pi */ ||
308 enc
==0x3c1 /* rho */ ||
309 enc
==0x3c5 /* upsilon */ ||
310 enc
==0x433 /* cyr ge */ ||
311 enc
==0x435 /* cyr e */ ||
312 enc
==0x436 /* cyr zhe */ ||
313 enc
==0x438 /* cyr i */ ||
314 enc
==0x43b /* cyr el */ ||
315 enc
==0x43d /* cyr en */ ||
316 enc
==0x43e /* cyr o */ ||
317 enc
==0x43f /* cyr pe */ ||
318 enc
==0x440 /* cyr er */ ||
319 enc
==0x441 /* cyr es */ ||
320 enc
==0x442 /* cyr te */ ||
321 enc
==0x443 /* cyr u */ ||
322 enc
==0x445 /* cyr ha */ ||
323 enc
==0x446 /* cyr tse */ ||
324 enc
==0x447 /* cyr che */ ||
325 enc
==0x448 /* cyr sha */ ||
326 enc
==0x449 /* cyr shcha */ ||
327 enc
==0x44a /* cyr hard sign */ ||
328 enc
==0x44b /* cyr yery */ ||
329 enc
==0x44c /* cyr soft sign */ ||
330 enc
==0x44d /* cyr reversed e */ ||
331 enc
==0x44f /* cyr ya */ ) {
332 AddBlue(b
.maxy
,xh
,enc
=='o' || enc
=='x');
337 /* the descent blue zone merges into the base zone */
338 MergeZones(descenth
,base
);
340 MergeZones(ascenth
,caph
);
341 MergeZones(digith
,caph
);
343 MergeZones(ascenth
,digith
);
344 MergeZones(xh
,digith
);
346 if ( otherblues
!=NULL
)
347 for ( i
=0; i
<10; ++i
)
349 for ( i
=0; i
<14; ++i
)
352 if ( otherblues
!=NULL
&& descenth
[2]!=0 ) {
353 otherblues
[0] = descenth
[3];
354 otherblues
[1] = descenth
[4];
357 if ( base
[2]==0 && (xh
[2]!=0 || ascenth
[2]!=0 || caph
[2]!=0 || digith
[2]!=0 )) {
358 /* base line blue value must be present if any other value is */
359 /* make one up if we don't have one */
363 } else if ( base
[2]!=0 ) {
373 blues
[i
++] = caph
[3];
374 blues
[i
++] = caph
[4];
376 if ( digith
[2]!=0 ) {
377 blues
[i
++] = digith
[3];
378 blues
[i
++] = digith
[4];
380 if ( ascenth
[2]!=0 ) {
381 blues
[i
++] = ascenth
[3];
382 blues
[i
++] = ascenth
[4];
385 for ( j
=0; j
<i
; j
+=2 ) for ( k
=j
+2; k
<i
; k
+=2 ) {
386 if ( blues
[j
]>blues
[k
] ) {
387 real temp
= blues
[j
];
391 blues
[j
+1] = blues
[k
+1];
397 void ElFreeEI(EIList
*el
) {
400 for ( e
= el
->edges
; e
!=NULL
; e
= next
) {
406 static int EIAddEdge(Spline
*spline
, real tmin
, real tmax
, EIList
*el
) {
407 EI
*new = gcalloc(1,sizeof(EI
));
410 real dxdtmin
, dxdtmax
, dydtmin
, dydtmax
;
412 new->spline
= spline
;
416 s
= &spline
->splines
[1];
417 if (( dydtmin
= (3*s
->a
*tmin
+ 2*s
->b
)*tmin
+ s
->c
)<0 ) dydtmin
= -dydtmin
;
418 if (( dydtmax
= (3*s
->a
*tmax
+ 2*s
->b
)*tmax
+ s
->c
)<0 ) dydtmax
= -dydtmax
;
419 s
= &spline
->splines
[0];
420 if (( dxdtmin
= (3*s
->a
*tmin
+ 2*s
->b
)*tmin
+ s
->c
)<0 ) dxdtmin
= -dxdtmin
;
421 if (( dxdtmax
= (3*s
->a
*tmax
+ 2*s
->b
)*tmax
+ s
->c
)<0 ) dxdtmax
= -dxdtmax
;
423 /*s = &spline->splines[0];*/
424 min
= ((s
->a
* tmin
+ s
->b
)* tmin
+ s
->c
)* tmin
+ s
->d
;
425 max
= ((s
->a
* tmax
+ s
->b
)* tmax
+ s
->c
)* tmax
+ s
->d
;
426 if ( tmax
==1 ) max
= spline
->to
->me
.x
; /* beware rounding errors */
427 if ( !el
->leavetiny
&& floor(min
)==floor(max
) ) { /* If it doesn't cross a pixel boundary then it might as well be vertical */
428 if ( tmin
==0 ) max
= min
;
429 else if ( tmax
==1 ) min
= max
;
437 temp
= min
; min
= max
; max
=temp
;
439 if ( !el
->leavetiny
&& min
+1>max
) new->almostvert
= true;
440 if ( 40*dxdtmin
<dydtmin
) new->vertattmin
= true;
441 if ( 40*dxdtmax
<dydtmax
) new->vertattmax
= true;
442 /*if ( new->vertattmin && new->vertattmax && s->a==0 && s->b==0 ) new->almostvert = true;*/
443 new->coordmin
[0] = min
; new->coordmax
[0] = max
;
444 if ( el
->coordmin
[0]>min
)
445 el
->coordmin
[0] = min
;
446 if ( el
->coordmax
[0]<max
)
447 el
->coordmax
[0] = max
;
449 s
= &spline
->splines
[1];
450 min
= ((s
->a
* tmin
+ s
->b
)* tmin
+ s
->c
)* tmin
+ s
->d
;
451 max
= ((s
->a
* tmax
+ s
->b
)* tmax
+ s
->c
)* tmax
+ s
->d
;
452 if ( tmax
==1 ) max
= spline
->to
->me
.y
;
453 if ( !el
->leavetiny
&& floor(min
)==floor(max
) ) { /* If it doesn't cross a pixel boundary then it might as well be horizontal */
454 if ( tmin
==0 ) max
= min
;
455 else if ( tmax
==1 ) min
= max
;
463 temp
= min
; min
= max
; max
=temp
;
465 if ( !el
->leavetiny
&& min
+1>max
) new->almosthor
= true;
466 if ( 40*dydtmin
<dxdtmin
) new->horattmin
= true;
467 if ( 40*dydtmax
<dxdtmax
) new->horattmax
= true;
468 /*if ( new->horattmin && new->horattmax && s->a==0 && s->b==0 ) new->almosthor = true;*/
469 new->coordmin
[1] = min
; new->coordmax
[1] = max
;
470 if ( el
->coordmin
[1]>min
)
471 el
->coordmin
[1] = min
;
472 if ( el
->coordmax
[1]<max
)
473 el
->coordmax
[1] = max
;
475 if ( new->hor
&& new->vert
) {
476 /* This spline is too small for us to notice */
480 new->next
= el
->edges
;
483 if ( el
->splinelast
!=NULL
)
484 el
->splinelast
->splinenext
= new;
485 el
->splinelast
= new;
486 if ( el
->splinefirst
==NULL
)
487 el
->splinefirst
= new;
493 static void EIAddSpline(Spline
*spline
, EIList
*el
) {
494 extended ts
[6], temp
;
495 int i
, j
, base
, last
;
497 ts
[0] = 0; ts
[5] = 1.0;
498 SplineFindExtrema(&spline
->splines
[0],&ts
[1],&ts
[2]);
499 SplineFindExtrema(&spline
->splines
[1],&ts
[3],&ts
[4]);
500 /* avoid teeny tiny segments, they just confuse us */
501 SplineRemoveExtremaTooClose(&spline
->splines
[0],&ts
[1],&ts
[2]);
502 SplineRemoveExtremaTooClose(&spline
->splines
[1],&ts
[3],&ts
[4]);
503 for ( i
=0; i
<4; ++i
) for ( j
=i
+1; j
<5; ++j
) {
510 for ( base
=0; ts
[base
]==-1; ++base
);
511 for ( i
=5; i
>base
; --i
) {
512 if ( ts
[i
]==ts
[i
-1] ) {
513 for ( j
=i
-1; j
>base
; --j
)
520 for ( i
=base
; i
<5 ; ++i
)
521 if ( EIAddEdge(spline
,ts
[last
],ts
[i
+1],el
) )
525 void ELFindEdges(SplineChar
*sc
, EIList
*el
) {
526 Spline
*spline
, *first
;
530 if ( sc
->layers
[el
->layer
].splines
==NULL
)
533 el
->coordmin
[0] = el
->coordmax
[0] = sc
->layers
[el
->layer
].splines
->first
->me
.x
;
534 el
->coordmin
[1] = el
->coordmax
[1] = sc
->layers
[el
->layer
].splines
->first
->me
.y
;
536 for ( spl
= sc
->layers
[el
->layer
].splines
; spl
!=NULL
; spl
= spl
->next
) if ( spl
->first
->prev
!=NULL
&& spl
->first
->prev
->from
!=spl
->first
) {
538 for ( spline
= spl
->first
->next
; spline
!=NULL
&& spline
!=first
; spline
=spline
->to
->next
) {
539 EIAddSpline(spline
,el
);
540 if ( first
==NULL
) first
= spline
;
542 if ( el
->splinefirst
!=NULL
&& spl
->first
->prev
!=NULL
)
543 el
->splinelast
->splinenext
= el
->splinefirst
;
544 el
->splinelast
= NULL
; el
->splinefirst
= NULL
;
548 static int IsBiggerSlope(EI
*test
, EI
*base
, int major
) {
550 real tdo
, tdm
, bdo
, bdm
, t
;
552 if (( major
&& test
->vup
) || (!major
&& test
->hup
))
556 tdm
= (3*test
->spline
->splines
[major
].a
*t
+ 2*test
->spline
->splines
[major
].b
)*t
+ test
->spline
->splines
[major
].c
;
557 tdo
= (3*test
->spline
->splines
[other
].a
*t
+ 2*test
->spline
->splines
[other
].b
)*t
+ test
->spline
->splines
[other
].c
;
559 if (( major
&& base
->vup
) || (!major
&& base
->hup
))
563 bdm
= (3*base
->spline
->splines
[major
].a
*t
+ 2*base
->spline
->splines
[major
].b
)*t
+ base
->spline
->splines
[major
].c
;
564 bdo
= (3*base
->spline
->splines
[other
].a
*t
+ 2*base
->spline
->splines
[other
].b
)*t
+ base
->spline
->splines
[other
].c
;
566 if ( tdm
==0 && bdm
==0 )
573 return( tdo
/tdm
> bdo
/bdm
);
576 void ELOrder(EIList
*el
, int major
) {
579 EI
*ei
, *prev
, *test
;
581 el
->low
= floor(el
->coordmin
[major
]); el
->high
= ceil(el
->coordmax
[major
]);
582 el
->cnt
= el
->high
-el
->low
+1;
583 el
->ordered
= gcalloc(el
->cnt
,sizeof(EI
*));
584 el
->ends
= gcalloc(el
->cnt
,1);
586 for ( ei
= el
->edges
; ei
!=NULL
; ei
=ei
->next
) {
587 pos
= ceil(ei
->coordmax
[major
])-el
->low
;
588 el
->ends
[pos
] = true;
589 pos
= floor(ei
->coordmin
[major
])-el
->low
;
590 ei
->ocur
= (ei
->hup
== ei
->vup
)?ei
->coordmin
[other
]:ei
->coordmax
[other
];
591 ei
->tcur
= ((major
&& ei
->vup
) || (!major
&& ei
->hup
)) ?
595 ei
->hv
= (ei
->vert
|| ei
->almostvert
);
596 ei
->hvbottom
= ei
->vup
? ei
->vertattmin
: ei
->vertattmax
;
597 ei
->hvtop
=!ei
->vup
? ei
->vertattmin
: ei
->vertattmax
;
598 if ( ei
->hor
|| ei
->almosthor
)
602 ei
->hv
= (ei
->hor
|| ei
->almosthor
);
603 ei
->hvbottom
= ei
->hup
? ei
->horattmin
: ei
->horattmax
;
604 ei
->hvtop
=!ei
->hup
? ei
->horattmin
: ei
->horattmax
;
605 if ( ei
->vert
|| ei
->almostvert
)
608 if ( el
->ordered
[pos
]==NULL
|| ei
->ocur
<el
->ordered
[pos
]->ocur
) {
609 ei
->ordered
= el
->ordered
[pos
];
610 el
->ordered
[pos
] = ei
;
612 for ( prev
=el
->ordered
[pos
], test
= prev
->ordered
; test
!=NULL
;
613 prev
= test
, test
= test
->ordered
) {
614 if ( test
->ocur
>ei
->ocur
||
615 (test
->ocur
==ei
->ocur
&& IsBiggerSlope(test
,ei
,major
)))
624 static HintInstance
*HIMerge(HintInstance
*into
, HintInstance
*hi
) {
625 HintInstance
*n
, *first
= NULL
, *last
= NULL
;
627 while ( into
!=NULL
&& hi
!=NULL
) {
628 if ( into
->begin
<hi
->begin
) {
646 } else if ( hi
!=NULL
) {
655 StemInfo
*HintCleanup(StemInfo
*stem
,int dosort
,int instance_count
) {
656 StemInfo
*s
, *p
=NULL
, *t
, *pt
, *sn
;
659 for ( s
=stem
; s
!=NULL
; p
=s
, s
=s
->next
) {
661 s
->start
+= s
->width
;
662 s
->width
= -s
->width
;
665 s
->reordered
= false;
666 if ( p
!=NULL
&& p
->start
> s
->start
)
670 for ( p
=NULL
, s
=stem
; s
!=NULL
; p
=s
, s
= sn
) {
672 for ( pt
=s
, t
=sn
; t
!=NULL
; pt
=t
, t
=t
->next
) {
673 if ( instance_count
>1 &&
674 t
->u
.unblended
!=NULL
&& s
->u
.unblended
!=NULL
) {
675 int temp
= UnblendedCompare((*t
->u
.unblended
)[0],(*s
->u
.unblended
)[0],instance_count
);
677 swap
= UnblendedCompare((*t
->u
.unblended
)[1],(*s
->u
.unblended
)[1],instance_count
);
680 } else if ( t
->start
<s
->start
)
682 else if ( t
->start
>s
->start
)
685 swap
= (t
->width
<s
->width
);
699 pt
= s
; /* swap s and t */
705 /* Remove duplicates */
706 if ( stem
!=NULL
) for ( p
=stem
, s
=stem
->next
; s
!=NULL
; s
= sn
) {
708 if ( p
->start
==s
->start
&& p
->width
==s
->width
&& p
->hintnumber
==s
->hintnumber
) {
709 p
->where
= HIMerge(p
->where
,s
->where
);
720 real
EITOfNextMajor(EI
*e
, EIList
*el
, real sought_m
) {
721 /* We want to find t so that Mspline(t) = sought_m */
722 /* the curve is monotonic */
723 Spline1D
*msp
= &e
->spline
->splines
[el
->major
];
728 if ( msp
->a
==0 && msp
->b
==0 ) {
730 IError("Hor/Vert line when not expected");
733 new_t
= (sought_m
-msp
->d
)/(msp
->c
);
737 t_mmax
= e
->up
?e
->tmax
:e
->tmin
;
738 t_mmin
= e
->up
?e
->tmin
:e
->tmax
;
739 /* sought_m += el->low; */
742 new_t
= (t_mmin
+t_mmax
)/2;
743 found_m
= ( ((msp
->a
*new_t
+msp
->b
)*new_t
+msp
->c
)*new_t
+ msp
->d
);
744 if ( found_m
>sought_m
-.001 && found_m
<sought_m
+.001 )
746 if ( found_m
> sought_m
) {
751 if ( t_mmax
==t_mmin
) {
752 IError("EITOfNextMajor failed! on %s", el
->sc
!=NULL
?el
->sc
->name
:"Unknown" );
758 EI
*EIActiveListReorder(EI
*active
,int *change
) {
763 if ( active
!=NULL
) {
767 for ( pr
=NULL
, apt
=active
; apt
->aenext
!=NULL
; ) {
768 if ( apt
->ocur
<= apt
->aenext
->ocur
) {
772 } else if ( pr
==NULL
) {
773 active
= apt
->aenext
;
774 apt
->aenext
= apt
->aenext
->aenext
;
775 active
->aenext
= apt
;
777 /* don't need to set any, since this reorder can't disorder the list */
780 pr
->aenext
= apt
->aenext
;
781 apt
->aenext
= apt
->aenext
->aenext
;
782 pr
->aenext
->aenext
= apt
;
783 any
= *change
= true;
792 EI
*EIActiveEdgesRefigure(EIList
*el
, EI
*active
,real i
,int major
, int *_change
) {
794 int change
= false, subchange
;
797 /* first remove any entry which doesn't intersect the new scan line */
798 /* (ie. stopped on last line) */
799 for ( pr
=NULL
, apt
=active
; apt
!=NULL
; apt
= apt
->aenext
) {
800 if ( apt
->coordmax
[major
]<i
+el
->low
) {
802 active
= apt
->aenext
;
804 pr
->aenext
= apt
->aenext
;
809 /* then move the active list to the next line */
810 for ( apt
=active
; apt
!=NULL
; apt
= apt
->aenext
) {
811 Spline1D
*osp
= &apt
->spline
->splines
[other
];
812 apt
->tcur
= EITOfNextMajor(apt
,el
,i
+el
->low
);
813 apt
->ocur
= ( ((osp
->a
*apt
->tcur
+osp
->b
)*apt
->tcur
+osp
->c
)*apt
->tcur
+ osp
->d
);
816 active
= EIActiveListReorder(active
,&subchange
);
817 if ( subchange
) change
= true;
819 /* Insert new nodes */
820 if ( el
->ordered
[(int) i
]!=NULL
) change
= true;
821 for ( pr
=NULL
, apt
=active
, npt
=el
->ordered
[(int) i
]; apt
!=NULL
&& npt
!=NULL
; ) {
822 if ( npt
->ocur
<apt
->ocur
) {
835 while ( npt
!=NULL
) {
849 int EISkipExtremum(EI
*e
, real i
, int major
) {
850 EI
*n
= e
->aenext
, *t
;
855 (ceil(e
->coordmin
[major
])==i
|| floor(e
->coordmin
[major
])==i
|| floor(e
->coordmax
[major
])==i
|| ceil(e
->coordmax
[major
])==i
) &&
856 (ceil(n
->coordmin
[major
])==i
|| floor(n
->coordmin
[major
])==i
|| floor(n
->coordmax
[major
])==i
|| ceil(n
->coordmax
[major
])==i
) ) {
858 (n
==e
->splinenext
&& n
->tmin
==e
->tmax
&&
859 n
->tcur
<n
->tmin
+.2 && e
->tcur
>e
->tmax
-.2 ) ||
860 (n
->splinenext
==e
&& n
->tmax
==e
->tmin
&&
861 n
->tcur
>n
->tmax
-.2 && e
->tcur
<e
->tmin
+.2 ) )
862 return( n
->up
!=e
->up
);
863 /* can be separated by a horizontal/vertical line in the other direction */
864 if ( n
->tmax
==1 && e
->tmin
==0 && n
->tcur
>.8 && e
->tcur
<.2) {
866 while ( (t
= t
->splinenext
)!=e
) {
867 if ( t
==NULL
|| t
==n
||
868 (major
&& !t
->hor
) || ( !major
&& !t
->vert
))
871 return( n
->up
!=e
->up
);
872 } else if ( n
->tmin
==0 && e
->tmax
==1 && n
->tcur
<.2 && e
->tcur
>.8) {
874 while ( (t
= t
->splinenext
)!=n
) {
875 if ( t
==NULL
|| t
==e
||
876 (major
&& !t
->hor
) || ( !major
&& !t
->vert
))
879 return( n
->up
!=e
->up
);
886 real
HIlen( StemInfo
*stems
) {
890 for ( hi
=stems
->where
; hi
!=NULL
; hi
= hi
->next
)
891 len
+= hi
->end
-hi
->begin
;
895 real
HIoverlap( HintInstance
*mhi
, HintInstance
*thi
) {
900 for ( ; mhi
!=NULL
; mhi
= mhi
->next
) {
901 for ( hi
= thi
; hi
!=NULL
&& hi
->begin
<=mhi
->end
; hi
= hi
->next
) {
902 if ( hi
->end
<mhi
->begin
) {
906 s
= hi
->begin
<mhi
->begin
?mhi
->begin
:hi
->begin
;
907 e
= hi
->end
>mhi
->end
?mhi
->end
:hi
->end
;
909 continue; /* Shouldn't happen */
917 int StemListAnyConflicts(StemInfo
*stems
) {
922 for ( s
=stems
; s
!=NULL
; s
=s
->next
)
923 s
->hasconflicts
= false;
924 while ( stems
!=NULL
) {
925 end
= stems
->width
<0 ? stems
->start
: stems
->start
+stems
->width
;
926 for ( s
=stems
->next
; s
!=NULL
&& (s
->width
>0 ? s
->start
: s
->start
+s
->width
)<end
; s
=s
->next
) {
927 stems
->hasconflicts
= true;
928 s
->hasconflicts
= true;
936 static HintInstance
*SCGuessHintPoints(SplineChar
*sc
, int layer
, StemInfo
*stem
,int major
, int off
) {
937 SplinePoint
*starts
[20], *ends
[20];
939 SplinePointList
*spl
;
940 SplinePoint
*sp
, *np
;
941 int sm
, wm
, i
, j
, val
;
943 HintInstance
*head
, *test
, *cur
, *prev
;
945 for ( spl
=sc
->layers
[layer
].splines
; spl
!=NULL
; spl
=spl
->next
) {
946 for ( sp
=spl
->first
; ; sp
= np
) {
947 coord
= (major
?sp
->me
.x
:sp
->me
.y
);
948 sm
= coord
>=stem
->start
-off
&& coord
<=stem
->start
+off
;
949 wm
= coord
>=stem
->start
+stem
->width
-off
&& coord
<=stem
->start
+stem
->width
+off
;
954 if ( sp
->next
==NULL
)
957 if ( np
==spl
->first
)
963 for ( i
=0; i
<spt
; ++i
) {
965 for ( j
=0; j
<ept
; ++j
) {
966 if ( major
&& starts
[i
]->me
.y
>=ends
[j
]->me
.y
-1 && starts
[i
]->me
.y
<=ends
[j
]->me
.y
+1 ) {
967 val
= starts
[i
]->me
.y
;
969 } else if ( !major
&& starts
[i
]->me
.x
>=ends
[j
]->me
.x
-1 && starts
[i
]->me
.x
<=ends
[j
]->me
.x
+1 ) {
970 val
= starts
[i
]->me
.x
;
974 if ( (unsigned)val
!=0x80000000 ) {
975 for ( prev
=NULL
, test
=head
; test
!=NULL
&& val
>test
->begin
; prev
=test
, test
=test
->next
);
976 if ( test
==NULL
|| val
!=test
->begin
) {
977 cur
= chunkalloc(sizeof(HintInstance
));
978 cur
->begin
= cur
->end
= val
;
980 if ( prev
==NULL
) head
= cur
;
981 else prev
->next
= cur
;
988 static void SCGuessHintInstancesLight(SplineChar
*sc
, int layer
, StemInfo
*stem
,int major
) {
989 SplinePointList
*spl
;
990 SplinePoint
*sp
, *np
;
993 HintInstance
*s
=NULL
, *w
=NULL
, *cur
, *p
, *t
, *n
, *w2
;
994 /* We've got a hint (from somewhere, old data, reading in a font, user specified etc.) */
995 /* but we don't have HintInstance info. So see if we can find those data */
996 /* Will get confused by stems with holes in them (for example if you make */
997 /* a hint from one side of an H to the other, it will get the whole thing */
998 /* not just the cross stem) */
1000 for ( spl
=sc
->layers
[layer
].splines
; spl
!=NULL
; spl
=spl
->next
) {
1001 for ( sp
=spl
->first
; ; sp
= np
) {
1002 sm
= (major
?sp
->me
.x
:sp
->me
.y
)==stem
->start
;
1003 wm
= (major
?sp
->me
.x
:sp
->me
.y
)==stem
->start
+stem
->width
;
1004 if ( sp
->next
==NULL
)
1009 if ( np
->me
.y
==sp
->me
.y
) {
1010 ob
= sp
->me
.x
; oe
= np
->me
.x
;
1011 } else if ( sp
->nextcp
.y
==sp
->me
.y
) {
1012 ob
= sp
->me
.x
; oe
= (sp
->me
.x
+sp
->nextcp
.x
)/2;
1013 if ( sp
->prevcp
.y
==sp
->me
.y
)
1014 ob
= (sp
->prevcp
.x
+sp
->me
.x
)/2;
1015 } else if ( sp
->prevcp
.y
==sp
->me
.y
) {
1016 ob
= sp
->me
.x
; oe
= (sp
->prevcp
.x
+sp
->me
.x
)/2;
1020 if ( np
->me
.x
==sp
->me
.x
) {
1021 ob
= sp
->me
.y
; oe
= np
->me
.y
;
1022 } else if ( sp
->nextcp
.x
==sp
->me
.x
) {
1023 ob
= sp
->me
.y
; oe
= (sp
->nextcp
.y
+sp
->me
.y
)/2;
1024 if ( sp
->prevcp
.x
==sp
->me
.x
)
1025 ob
= (sp
->prevcp
.y
+sp
->me
.y
)/2;
1026 } else if ( sp
->prevcp
.x
==sp
->me
.x
) {
1027 ob
= sp
->me
.y
; oe
= (sp
->prevcp
.y
+sp
->me
.y
)/2;
1033 cur
= chunkalloc(sizeof(HintInstance
));
1034 if ( ob
>oe
) { real temp
=ob
; ob
=oe
; oe
=temp
;}
1038 if ( s
==NULL
|| s
->begin
>cur
->begin
) {
1043 for ( t
=s
->next
; t
!=NULL
&& t
->begin
<cur
->begin
; p
=t
, t
=t
->next
);
1044 p
->next
= cur
; cur
->next
= t
;
1047 if ( w
==NULL
|| w
->begin
>cur
->begin
) {
1052 for ( t
=w
->next
; t
!=NULL
&& t
->begin
<cur
->begin
; p
=t
, t
=t
->next
);
1053 p
->next
= cur
; cur
->next
= t
;
1057 if ( np
==spl
->first
)
1062 /* Now we know what the right side of the stem looks like, and we know */
1063 /* what the left side looks like. They may not look the same (H for example) */
1064 /* Figure out the set where both are active */
1065 /* Unless it's a ghost hint */
1066 if ( stem
->width
==20 && s
==NULL
&& w
!=NULL
) {
1069 } else if ( stem
->width
==21 && s
!=NULL
&& w
==NULL
) {
1071 } else for ( p
=NULL
, t
=s
; t
!=NULL
; t
=n
) {
1073 for ( w2
=w
; w2
!=NULL
&& w2
->begin
<t
->end
; w2
=w2
->next
) {
1074 if ( w2
->end
<=t
->begin
)
1076 if ( w2
->begin
<=t
->begin
&& w2
->end
>=t
->end
) {
1080 if ( w2
->begin
>=t
->begin
)
1081 t
->begin
= w2
->begin
;
1082 if ( w2
->end
<=t
->end
) {
1083 cur
= chunkalloc(sizeof(HintInstance
));
1084 cur
->begin
= w2
->end
;
1093 if ( w2
!=NULL
&& w2
->begin
>=t
->end
)
1095 if ( w2
==NULL
&& w
!=NULL
) {
1096 HintInstance
*best
= NULL
;
1097 double best_off
=1e10
, off
;
1098 for ( w2
=w
; w2
!=NULL
; w2
=w2
->next
) {
1099 if ( w2
->end
<=t
->begin
)
1100 off
= t
->begin
- w2
->end
;
1102 off
= w2
->begin
- t
->end
;
1103 if ( best
==NULL
&& off
<best_off
) {
1108 if ( best
!=NULL
&& best_off
<stem
->width
) {
1110 if( w2
->begin
<t
->begin
)
1111 t
->begin
= w2
->begin
;
1112 if ( w2
->end
>t
->end
)
1117 /* No match for t (or if there were it wasn't complete) get rid */
1118 /* of what's left of t */
1119 chunkfree(t
,sizeof(*t
));
1129 chunkfree(w
,sizeof(*w
));
1133 /* If we couldn't find anything, then see if there are two points which */
1134 /* have the same x or y value and whose other coordinates match those of */
1136 /* Surprisingly many fonts have hints which don't accurately match the */
1137 /* points. Perhaps BlueFuzz (default 1) applies here too */
1138 for ( off
=0; off
<1 && s
==NULL
; ++off
)
1139 s
= SCGuessHintPoints(sc
,layer
, stem
,major
,off
);
1144 void SCGuessHHintInstancesList(SplineChar
*sc
,int layer
) {
1146 for ( h
= sc
->hstem
; h
!=NULL
; h
=h
->next
) {
1147 if ( h
->where
==NULL
) {
1148 SCGuessHintInstancesLight( sc
,layer
,h
,false );
1153 void SCGuessVHintInstancesList(SplineChar
*sc
,int layer
) {
1155 for ( h
= sc
->vstem
; h
!=NULL
; h
=h
->next
) {
1156 if ( h
->where
==NULL
) {
1157 SCGuessHintInstancesLight( sc
,layer
,h
,true );
1163 static void _SCClearHintMasks(SplineChar
*sc
,int layer
, int counterstoo
) {
1168 if ( counterstoo
) {
1169 free(sc
->countermasks
);
1170 sc
->countermasks
= NULL
; sc
->countermask_cnt
= 0;
1173 for ( spl
= sc
->layers
[layer
].splines
; spl
!=NULL
; spl
=spl
->next
) {
1174 for ( sp
= spl
->first
; ; ) {
1175 chunkfree(sp
->hintmask
,sizeof(HintMask
));
1176 sp
->hintmask
= NULL
;
1177 if ( sp
->next
==NULL
)
1180 if ( sp
==spl
->first
)
1185 for ( ref
= sc
->layers
[layer
].refs
; ref
!=NULL
; ref
=ref
->next
) {
1186 for ( spl
= ref
->layers
[0].splines
; spl
!=NULL
; spl
=spl
->next
) {
1187 for ( sp
= spl
->first
; ; ) {
1188 chunkfree(sp
->hintmask
,sizeof(HintMask
));
1189 sp
->hintmask
= NULL
;
1190 if ( sp
->next
==NULL
)
1193 if ( sp
==spl
->first
)
1201 static void SCFigureSimpleCounterMasks(SplineChar
*sc
) {
1202 SplineChar
*scs
[MmMax
];
1203 int hadh3
, hadv3
, i
, vbase
;
1207 if ( sc
->countermask_cnt
!=0 )
1211 hadh3
= CvtPsStem3(NULL
,scs
,1,true,false);
1212 hadv3
= CvtPsStem3(NULL
,scs
,1,false,false);
1213 if ( hadh3
|| hadv3
) {
1214 memset(mask
,0,sizeof(mask
));
1215 if ( hadh3
) mask
[0] = 0x80|0x40|0x20;
1217 for ( h
=sc
->hstem
, vbase
=0; h
!=NULL
; h
=h
->next
, ++vbase
);
1218 for ( i
=0; i
<3 ; ++i
) {
1220 mask
[j
>>3] |= (0x80>>(j
&7));
1223 sc
->countermask_cnt
= 1;
1224 sc
->countermasks
= galloc(sizeof(HintMask
));
1225 memcpy(sc
->countermasks
[0],mask
,sizeof(HintMask
));
1230 void SCClearHintMasks(SplineChar
*sc
,int layer
,int counterstoo
) {
1231 MMSet
*mm
= sc
->parent
->mm
;
1235 _SCClearHintMasks(sc
,layer
,counterstoo
);
1237 for ( i
=0; i
<mm
->instance_count
; ++i
) {
1238 if ( sc
->orig_pos
<mm
->instances
[i
]->glyphcnt
)
1239 _SCClearHintMasks(mm
->instances
[i
]->glyphs
[sc
->orig_pos
],layer
,counterstoo
);
1241 if ( sc
->orig_pos
<mm
->normal
->glyphcnt
)
1242 _SCClearHintMasks(mm
->normal
->glyphs
[sc
->orig_pos
],layer
,counterstoo
);
1246 static StemInfo
*OnHHint(SplinePoint
*sp
, StemInfo
*s
) {
1247 StemInfo
*possible
=NULL
;
1253 for ( ; s
!=NULL
; s
=s
->next
) {
1254 if ( sp
->me
.y
<s
->start
)
1256 if ( s
->start
==sp
->me
.y
|| s
->start
+s
->width
==sp
->me
.y
) {
1257 if ( !s
->hasconflicts
)
1259 for ( hi
=s
->where
; hi
!=NULL
; hi
=hi
->next
) {
1260 if ( hi
->begin
<=sp
->me
.x
&& hi
->end
>=sp
->me
.x
)
1270 static StemInfo
*OnVHint(SplinePoint
*sp
, StemInfo
*s
) {
1271 StemInfo
*possible
=NULL
;
1277 for ( ; s
!=NULL
; s
=s
->next
) {
1278 if ( sp
->me
.x
<s
->start
)
1280 if ( s
->start
==sp
->me
.x
|| s
->start
+s
->width
==sp
->me
.x
) {
1281 if ( !s
->hasconflicts
)
1283 for ( hi
=s
->where
; hi
!=NULL
; hi
=hi
->next
) {
1284 if ( hi
->begin
<=sp
->me
.y
&& hi
->end
>=sp
->me
.y
)
1294 /* Does h have a conflict with any of the stems in the list which have bits */
1295 /* set in the mask */
1296 static int ConflictsWithMask(StemInfo
*stems
, HintMask mask
,StemInfo
*h
) {
1297 while ( stems
!=NULL
&& stems
->start
<h
->start
+h
->width
) {
1298 if ( stems
->start
+stems
->width
>=h
->start
&& stems
!=h
) {
1299 if ( stems
->hintnumber
!=-1 &&
1300 (mask
[stems
->hintnumber
>>3]&(0x80>>(stems
->hintnumber
&7))) )
1303 stems
= stems
->next
;
1308 /* All instances of a MM set must have the same hint mask at all points */
1309 static void FigureHintMask(SplineChar
*scs
[MmMax
], SplinePoint
*to
[MmMax
], int instance_count
,
1315 memset(mask
,'\0',sizeof(HintMask
));
1317 /* Install all hints that are always active */
1319 SplineChar
*sc
= scs
[i
];
1324 for ( s
=sc
->hstem
; s
!=NULL
; s
=s
->next
)
1325 if ( s
->hintnumber
!=-1 && !s
->hasconflicts
)
1326 mask
[s
->hintnumber
>>3] |= (0x80>>(s
->hintnumber
&7));
1327 for ( s
=sc
->vstem
; s
!=NULL
; s
=s
->next
)
1328 if ( s
->hintnumber
!=-1 && !s
->hasconflicts
)
1329 mask
[s
->hintnumber
>>3] |= (0x80>>(s
->hintnumber
&7));
1331 if ( sc
->hconflicts
) {
1332 for ( sp
=to
[i
]; sp
!=NULL
; ) {
1333 s
= OnHHint(sp
,sc
->hstem
);
1334 if ( s
!=NULL
&& s
->hintnumber
!=-1 ) {
1335 if ( ConflictsWithMask(scs
[i
]->hstem
,mask
,s
))
1337 mask
[s
->hintnumber
>>3] |= (0x80>>(s
->hintnumber
&7));
1339 if ( sp
->next
==NULL
)
1346 if ( sc
->vconflicts
) {
1347 for ( sp
=to
[i
]; sp
!=NULL
; ) {
1348 s
= OnVHint(sp
,sc
->vstem
);
1349 if ( s
!=NULL
&& s
->hintnumber
!=-1 ) {
1350 if ( ConflictsWithMask(scs
[i
]->vstem
,mask
,s
))
1352 mask
[s
->hintnumber
>>3] |= (0x80>>(s
->hintnumber
&7));
1354 if ( sp
->next
==NULL
)
1362 for ( i
=0; i
<instance_count
; ++i
) if ( to
[i
]!=NULL
) {
1363 chunkfree(to
[i
]->hintmask
,sizeof(HintMask
));
1364 to
[i
]->hintmask
= chunkalloc(sizeof(HintMask
));
1365 memcpy(to
[i
]->hintmask
,mask
,sizeof(HintMask
));
1369 static int TestHintMask(SplineChar
*scs
[MmMax
], SplinePoint
*to
[MmMax
], int instance_count
,
1371 StemInfo
*h
=NULL
, *v
=NULL
;
1374 for ( i
=0; i
<instance_count
; ++i
) {
1375 SplineChar
*sc
= scs
[i
];
1377 if ( sc
==NULL
|| (!sc
->hconflicts
&& !sc
->vconflicts
))
1380 /* Does this point lie on any hints? */
1381 if ( scs
[i
]->hconflicts
)
1382 h
= OnHHint(to
[i
],sc
->hstem
);
1383 if ( scs
[i
]->vconflicts
)
1384 v
= OnVHint(to
[i
],sc
->vstem
);
1386 /* Need to set this hint */
1387 if ( (h
!=NULL
&& h
->hintnumber
!=-1 && (mask
[h
->hintnumber
>>3]&(0x80>>(h
->hintnumber
&7)))==0 ) ||
1388 (v
!=NULL
&& v
->hintnumber
!=-1 && (mask
[v
->hintnumber
>>3]&(0x80>>(v
->hintnumber
&7)))==0 ))
1391 if ( i
==instance_count
) /* All hint masks were ok */
1394 FigureHintMask(scs
,to
,instance_count
,mask
);
1398 static void UnnumberHints(SplineChar
*sc
) {
1401 for ( h
=sc
->hstem
; h
!=NULL
; h
=h
->next
)
1403 for ( h
=sc
->vstem
; h
!=NULL
; h
=h
->next
)
1407 static int NumberHints(SplineChar
*sc
) {
1411 for ( h
=sc
->hstem
; h
!=NULL
; h
=h
->next
)
1412 h
->hintnumber
= hcnt
>=HntMax
? -1 : hcnt
++;
1413 for ( h
=sc
->vstem
; h
!=NULL
; h
=h
->next
)
1414 h
->hintnumber
= hcnt
>=HntMax
? -1 : hcnt
++;
1418 static void UntickHints(SplineChar
*sc
) {
1421 for ( h
=sc
->hstem
; h
!=NULL
; h
=h
->next
)
1423 for ( h
=sc
->vstem
; h
!=NULL
; h
=h
->next
)
1429 struct coords
*next
;
1432 typedef struct mmh
{
1433 StemInfo
*hints
[MmMax
], *map
[MmMax
];
1434 struct coords
*where
;
1438 static void AddCoord(MMH
*mmh
,SplinePoint
*sps
[MmMax
],int instance_count
, int ish
) {
1439 struct coords
*coords
;
1442 coords
= chunkalloc(sizeof(struct coords
));
1443 coords
->next
= mmh
->where
;
1444 mmh
->where
= coords
;
1446 for ( i
=0; i
<instance_count
; ++i
)
1447 coords
->coords
[i
] = sps
[i
]->me
.x
;
1449 for ( i
=0; i
<instance_count
; ++i
)
1450 coords
->coords
[i
] = sps
[i
]->me
.y
;
1453 static MMH
*AddHintSet(MMH
*hints
,StemInfo
*h
[MmMax
], int instance_count
,
1454 SplinePoint
*sps
[MmMax
], int ish
) {
1458 for ( i
=0; i
<instance_count
; ++i
)
1462 best
= NULL
; bestc
= 0;
1463 for ( test
=hints
; test
!=NULL
; test
=test
->next
) {
1465 for ( i
=0; i
<instance_count
; ++i
)
1466 if ( test
->hints
[i
]==h
[i
] )
1468 if ( cnt
==instance_count
) {
1469 AddCoord(test
,sps
,instance_count
,ish
);
1477 test
= chunkalloc(sizeof(MMH
));
1479 AddCoord(test
,sps
,instance_count
,ish
);
1480 for ( i
=0; i
<instance_count
; ++i
)
1481 test
->hints
[i
]=h
[i
];
1483 for ( i
=0; i
<instance_count
; ++i
) {
1484 if ( best
->hints
[i
]==h
[i
] ) {
1485 h
[i
]->hasconflicts
= true;
1486 test
->map
[i
] = chunkalloc(sizeof(StemInfo
));
1487 *test
->map
[i
] = *h
[i
];
1488 test
->map
[i
]->where
= NULL
;
1489 test
->map
[i
]->used
= true;
1490 h
[i
]->next
= test
->map
[i
];
1492 test
->map
[i
] = h
[i
];
1495 for ( i
=0; i
<instance_count
; ++i
)
1501 static int CompareMMH(MMH
*mmh1
,MMH
*mmh2
, int instance_count
) {
1504 if ( mmh1
->map
[0]==NULL
)
1506 if ( mmh2
->map
[0]==NULL
)
1509 for ( i
=0; i
<instance_count
; ++i
) {
1510 if ( mmh1
->map
[i
]->start
!=mmh2
->map
[i
]->start
) {
1511 if ( mmh1
->map
[i
]->start
> mmh2
->map
[i
]->start
)
1517 for ( i
=0; i
<instance_count
; ++i
) {
1518 if ( mmh1
->map
[i
]->width
!=mmh2
->map
[i
]->width
) {
1519 if ( mmh1
->map
[i
]->width
> mmh2
->map
[i
]->width
)
1528 static MMH
*SortMMH(MMH
*head
,int instance_count
) {
1529 MMH
*mmh
, *p
, *smallest
, *psmallest
, *test
, *ptest
;
1531 for ( mmh
= head
, p
=NULL
; mmh
!=NULL
; ) {
1532 smallest
= mmh
; psmallest
= p
;
1533 ptest
= mmh
; test
= mmh
->next
;
1534 while ( test
!=NULL
) {
1535 if ( CompareMMH(test
,smallest
,instance_count
)<0 ) {
1542 if ( smallest
!=mmh
) {
1547 if ( mmh
->next
==smallest
) {
1548 mmh
->next
= smallest
->next
;
1549 smallest
->next
= mmh
;
1552 mmh
->next
= smallest
->next
;
1553 smallest
->next
= test
;
1554 psmallest
->next
= mmh
;
1558 mmh
= smallest
->next
;
1563 static int NumberMMH(MMH
*mmh
,int hstart
,int instance_count
) {
1565 HintInstance
*hi
, *n
;
1566 struct coords
*coords
;
1568 while ( mmh
!=NULL
) {
1569 for ( i
=0; i
<instance_count
; ++i
) {
1570 StemInfo
*h
= mmh
->map
[i
];
1574 h
->hintnumber
= hstart
;
1576 for ( hi
=h
->where
; hi
!=NULL
; hi
=n
) {
1578 chunkfree(hi
,sizeof(HintInstance
));
1581 for ( coords
=mmh
->where
; coords
!=NULL
; coords
= coords
->next
) {
1582 hi
= chunkalloc(sizeof(HintInstance
));
1583 hi
->next
= h
->where
;
1585 hi
->begin
= coords
->coords
[i
]-1;
1586 hi
->end
= coords
->coords
[i
]+1;
1589 if ( mmh
->map
[0]!=NULL
) ++hstart
;
1595 static void SortMMH2(SplineChar
*scs
[MmMax
],MMH
*mmh
,int instance_count
,int ish
) {
1600 for ( i
=0; i
<instance_count
; ++i
) {
1601 for ( h
= ish
? scs
[i
]->hstem
: scs
[i
]->vstem
; h
!=NULL
; h
=n
) {
1603 if ( h
->hintnumber
==-1 )
1607 for ( m
= mmh
; m
!=NULL
; m
=m
->next
) {
1620 scs
[i
]->hstem
= NULL
;
1622 scs
[i
]->vstem
= NULL
;
1626 static void MMHFreeList(MMH
*mmh
) {
1628 struct coords
*c
, *n
;
1630 for ( ; mmh
!=NULL
; mmh
= mn
) {
1632 for ( c
=mmh
->where
; c
!=NULL
; c
=n
) {
1634 chunkfree(c
,sizeof(struct coords
));
1636 chunkfree(mmh
,sizeof(struct coords
));
1640 static void SplResolveSplitHints(SplineChar
*scs
[MmMax
], SplineSet
*spl
[MmMax
],
1641 int instance_count
, MMH
**hs
, MMH
**vs
) {
1642 SplinePoint
*to
[MmMax
];
1643 StemInfo
*h
[MmMax
], *v
[MmMax
];
1647 for ( i
=0; i
<instance_count
; ++i
) {
1649 to
[i
] = spl
[i
]->first
;
1654 for ( i
=0; i
<instance_count
; ++i
) {
1655 h
[i
] = OnHHint(to
[i
],scs
[i
]->hstem
);
1656 v
[i
] = OnVHint(to
[i
],scs
[i
]->vstem
);
1658 *hs
= AddHintSet(*hs
,h
,instance_count
,to
,true);
1659 *vs
= AddHintSet(*vs
,v
,instance_count
,to
,false);
1661 for ( i
=0; i
<instance_count
; ++i
) if ( to
[i
]!=NULL
) {
1662 if ( to
[i
]->next
==NULL
) to
[i
] = NULL
;
1664 to
[i
] = to
[i
]->next
->to
;
1665 if ( to
[i
]==spl
[i
]->first
) to
[i
] = NULL
;
1667 if ( to
[i
]!=NULL
) anymore
= true;
1673 for ( i
=0; i
<instance_count
; ++i
) {
1675 spl
[i
] = spl
[i
]->next
;
1676 if ( spl
[i
]!=NULL
) anymore
= true;
1683 static void ResolveSplitHints(SplineChar
*scs
[16],int layer
,int instance_count
) {
1684 /* It is possible for a single hint in one mm instance to split into two */
1685 /* in a different MM set. For example, we have two stems which happen */
1686 /* to line up in one instance but which do not in another instance. */
1687 /* It is even possible that there could be no instance with any conflicts */
1688 /* but some of the intermediate forms might conflict. */
1689 /* We can't deal (nor can postscript) with the case where hints change order*/
1690 SplinePointList
*spl
[MmMax
];
1691 RefChar
*ref
[MmMax
];
1692 int i
, hcnt
, hmax
=0, anymore
;
1693 MMH
*hs
=NULL
, *vs
=NULL
;
1695 for ( i
=0; i
<instance_count
; ++i
) {
1696 hcnt
= NumberHints(scs
[i
]);
1697 UntickHints(scs
[i
]);
1698 if ( hcnt
>hmax
) hmax
= hcnt
;
1699 spl
[i
] = scs
[i
]->layers
[layer
].splines
;
1704 SplResolveSplitHints(scs
,spl
,instance_count
,&hs
,&vs
);
1706 for ( i
=0; i
<instance_count
; ++i
) {
1707 ref
[i
] = scs
[i
]->layers
[layer
].refs
;
1708 if ( ref
[i
]!=NULL
) anymore
= true;
1711 for ( i
=0; i
<instance_count
; ++i
)
1712 spl
[i
] = ( ref
[i
]!=NULL
) ? ref
[i
]->layers
[0].splines
: NULL
;
1713 SplResolveSplitHints(scs
,spl
,instance_count
,&hs
,&vs
);
1715 for ( i
=0; i
<instance_count
; ++i
) {
1716 if ( ref
[i
]!=NULL
) {
1717 ref
[i
] = ref
[i
]->next
;
1718 if ( ref
[i
]!=NULL
) anymore
= true;
1723 for ( i
=0; i
<instance_count
; ++i
)
1724 UnnumberHints(scs
[i
]);
1725 hs
= SortMMH(hs
,instance_count
);
1726 vs
= SortMMH(vs
,instance_count
);
1727 hcnt
= NumberMMH(hs
,0,instance_count
);
1728 hcnt
= NumberMMH(vs
,hcnt
,instance_count
);
1729 SortMMH2(scs
,hs
,instance_count
,true);
1730 SortMMH2(scs
,vs
,instance_count
,false);
1735 static int SplFigureHintMasks(SplineChar
*scs
[MmMax
], SplineSet
*spl
[MmMax
],
1736 int instance_count
, HintMask mask
, int inited
) {
1737 SplinePoint
*to
[MmMax
];
1741 for ( i
=0; i
<instance_count
; ++i
) {
1742 if ( spl
[i
]!=NULL
) {
1743 SplineSetReverse(spl
[i
]);
1744 to
[i
] = spl
[i
]->first
;
1750 /* Assign the initial hint mask */
1751 if ( anymore
&& !inited
) {
1752 FigureHintMask(scs
,to
,instance_count
,mask
);
1757 for ( i
=0; i
<instance_count
; ++i
) {
1759 to
[i
] = spl
[i
]->first
;
1764 TestHintMask(scs
,to
,instance_count
,mask
);
1766 for ( i
=0; i
<instance_count
; ++i
) if ( to
[i
]!=NULL
) {
1767 if ( to
[i
]->next
==NULL
) to
[i
] = NULL
;
1769 to
[i
] = to
[i
]->next
->to
;
1770 if ( to
[i
]==spl
[i
]->first
) to
[i
] = NULL
;
1772 if ( to
[i
]!=NULL
) anymore
= true;
1778 for ( i
=0; i
<instance_count
; ++i
) {
1779 if ( spl
[i
]!=NULL
) {
1780 SplineSetReverse(spl
[i
]);
1781 spl
[i
] = spl
[i
]->next
;
1783 if ( spl
[i
]!=NULL
) {
1785 SplineSetReverse(spl
[i
]);
1794 void SCFigureHintMasks(SplineChar
*sc
,int layer
) {
1795 SplineChar
*scs
[MmMax
];
1796 SplinePointList
*spl
[MmMax
];
1797 RefChar
*ref
[MmMax
];
1798 MMSet
*mm
= sc
->parent
->mm
;
1799 int i
, instance_count
, conflicts
, anymore
, inited
;
1805 SCClearHintMasks(sc
,layer
,false);
1807 instance_count
= mm
->instance_count
;
1808 for ( i
=0; i
<instance_count
; ++i
)
1809 if ( sc
->orig_pos
< mm
->instances
[i
]->glyphcnt
) {
1810 scs
[i
] = mm
->instances
[i
]->glyphs
[sc
->orig_pos
];
1811 SCClearHintMasks(scs
[i
],layer
,false);
1813 ResolveSplitHints(scs
,layer
,instance_count
);
1816 for ( i
=0; i
<instance_count
; ++i
) {
1817 NumberHints(scs
[i
]);
1818 if ( scs
[i
]->hconflicts
|| scs
[i
]->vconflicts
)
1821 if ( !conflicts
&& instance_count
==1 ) { /* All hints always active */
1822 SCFigureSimpleCounterMasks(sc
);
1823 return; /* In an MM font we may still need to resolve things like different numbers of hints */
1826 for ( i
=0; i
<instance_count
; ++i
) {
1827 spl
[i
] = scs
[i
]->layers
[layer
].splines
;
1828 ref
[i
] = scs
[i
]->layers
[layer
].refs
;
1830 inited
= SplFigureHintMasks(scs
,spl
,instance_count
,mask
,false);
1832 for ( i
=0; i
<instance_count
; ++i
) {
1834 spl
[i
] = ref
[i
]->layers
[0].splines
;
1836 inited
= SplFigureHintMasks(scs
,spl
,instance_count
,mask
,inited
);
1838 for ( i
=0; i
<instance_count
; ++i
) {
1839 if ( ref
[i
]!=NULL
) {
1840 ref
[i
] = ref
[i
]->next
;
1841 if ( ref
[i
]!=NULL
) anymore
= true;
1847 if ( instance_count
==1 )
1848 SCFigureSimpleCounterMasks(sc
);
1853 static void FigureStems( SplineFont
*sf
, real snaps
[12], real cnts
[12],
1855 int i
, j
, k
, cnt
, smax
=0, smin
=2000;
1856 real stemwidths
[2000];
1857 StemInfo
*stems
, *test
;
1861 memset(stemwidths
,'\0',sizeof(stemwidths
));
1863 for ( i
=0; i
<sf
->glyphcnt
; ++i
) if ( sf
->glyphs
[i
]!=NULL
) {
1864 stems
= which
?sf
->glyphs
[i
]->hstem
:sf
->glyphs
[i
]->vstem
;
1865 for ( test
=stems
; test
!=NULL
; test
= test
->next
) if ( !test
->ghost
) {
1866 if ( (j
=test
->width
)<0 ) j
= -j
;
1869 for ( hi
=test
->where
; hi
!=NULL
; hi
=hi
->next
)
1870 len
+= hi
->end
-hi
->begin
;
1871 if ( len
==0 ) len
= 100;
1872 stemwidths
[j
] += len
;
1873 if ( smax
<j
) smax
=j
;
1874 if ( smin
>j
) smin
=j
;
1879 for ( i
=smin
, cnt
=0; i
<=smax
; ++i
) {
1880 if ( stemwidths
[i
]!=0 )
1885 /* Merge width windows */
1887 for ( i
=smin
; i
<=smax
; ++i
) if ( stemwidths
[i
]!=0 ) {
1888 if ( (j
= i
-windsize
)<0 ) j
=0;
1889 for ( ; j
<smax
&& j
<=i
+windsize
; ++j
)
1890 if ( stemwidths
[i
]<stemwidths
[j
] )
1892 if ( j
==smax
|| j
>i
+windsize
) {
1893 if ( (j
= i
-windsize
)<0 ) j
=0;
1894 for ( ; j
<smax
&& j
<=i
+windsize
; ++j
) if ( j
!=i
) {
1895 stemwidths
[i
] += stemwidths
[j
];
1900 /* Merge adjacent widths */
1901 for ( i
=smin
; i
<=smax
; ++i
) {
1902 if ( stemwidths
[i
]!=0 && i
<=smax
-1 && stemwidths
[i
+1]!=0 ) {
1903 if ( stemwidths
[i
]>stemwidths
[i
+1] ) {
1904 stemwidths
[i
] += stemwidths
[i
+1];
1905 stemwidths
[i
+1] = 0;
1907 if ( i
<=smax
-2 && stemwidths
[i
+2] && stemwidths
[i
+2]<stemwidths
[i
+1] ) {
1908 stemwidths
[i
+1] += stemwidths
[i
+2];
1909 stemwidths
[i
+2] = 0;
1911 stemwidths
[i
+1] += stemwidths
[i
];
1917 for ( i
=smin
, cnt
=0; i
<=smax
; ++i
) {
1918 if ( stemwidths
[i
]!=0 )
1923 for ( i
=smin
, cnt
=0; i
<=smax
; ++i
) {
1924 if ( stemwidths
[i
]!=0 ) {
1926 cnts
[cnt
++] = stemwidths
[i
];
1929 } else { real firstbiggest
=0;
1930 for ( cnt
= 0; cnt
<12; ++cnt
) {
1933 for ( i
=smin
; i
<=smax
; ++i
) {
1934 if ( stemwidths
[i
]>biggest
) { biggest
= stemwidths
[i
]; biggesti
=i
; }
1936 /* array must be sorted */
1937 if ( biggest
<firstbiggest
/6 )
1939 for ( j
=0; j
<cnt
; ++j
)
1940 if ( snaps
[j
]>biggesti
)
1942 for ( k
=cnt
-1; k
>=j
; --k
) {
1943 snaps
[k
+1] = snaps
[k
];
1946 snaps
[j
] = biggesti
;
1948 stemwidths
[biggesti
] = 0;
1949 if ( firstbiggest
==0 ) firstbiggest
= biggest
;
1952 for ( ; cnt
<12; ++cnt
) {
1958 void FindHStems( SplineFont
*sf
, real snaps
[12], real cnt
[12]) {
1959 FigureStems(sf
,snaps
,cnt
,1);
1962 void FindVStems( SplineFont
*sf
, real snaps
[12], real cnt
[12]) {
1963 FigureStems(sf
,snaps
,cnt
,0);
1966 static int IsFlexSmooth(SplinePoint
*sp
) {
1967 BasePoint nvec
, pvec
;
1968 double proj_same
, proj_normal
;
1970 if ( sp
->nonextcp
|| sp
->noprevcp
)
1971 return( false ); /* No continuity of slopes */
1973 nvec
.x
= sp
->nextcp
.x
- sp
->me
.x
; nvec
.y
= sp
->nextcp
.y
- sp
->me
.y
;
1974 pvec
.x
= sp
->me
.x
- sp
->prevcp
.x
; pvec
.y
= sp
->me
.y
- sp
->prevcp
.y
;
1976 /* Avoid cases where the slopes are 180 out of phase */
1977 if ( (proj_same
= nvec
.x
*pvec
.x
+ nvec
.y
*pvec
.y
)<=0 )
1979 if ( (proj_normal
= nvec
.x
*pvec
.y
- nvec
.y
*pvec
.x
)<0 )
1980 proj_normal
= -proj_normal
;
1982 /* Something is smooth if the normal projection is 0. Let's allow for */
1983 /* some rounding errors */
1984 if ( proj_same
>= 16*proj_normal
)
1990 static int _SplineCharIsFlexible(SplineChar
*sc
, int layer
, int blueshift
) {
1992 outer endpoints have same x (or y) values
1993 inner point must be less than 20 horizontal (v) units from the outer points
1994 inner point must also be less than BlueShift units (defaults to 7=>6)
1995 (can increase BlueShift up to 21)
1996 the inner point must be a local extremum
1997 the inner point's cps must be at the x (or y) value as the extremum
2000 /* We want long, nearly straight stems. If the end-points should not have
2001 continuous slopes, or if they do, they must be horizontal/vertical.
2002 This is an heuristic requirement, not part of Adobe's spec.
2005 SplinePoint
*sp
, *np
, *pp
;
2012 for ( spl
= sc
->layers
[layer
].splines
; spl
!=NULL
; spl
=spl
->next
) {
2013 if ( spl
->first
->prev
==NULL
) {
2014 /* Mark everything on the open path as inflexible */
2017 sp
->flexx
= sp
->flexy
= false;
2018 if ( sp
->next
==NULL
)
2022 continue; /* Ignore open paths */
2026 if ( sp
->next
==NULL
|| sp
->prev
==NULL
)
2029 pp
= sp
->prev
->from
;
2030 if ( !pp
->flexx
&& !pp
->flexy
) {
2031 sp
->flexy
= sp
->flexx
= 0;
2033 if ( RealNear(sp
->nextcp
.x
,sp
->me
.x
) &&
2034 RealNear(sp
->prevcp
.x
,sp
->me
.x
) &&
2035 RealNear(np
->me
.x
,pp
->me
.x
) &&
2036 !RealNear(np
->me
.x
,sp
->me
.x
) &&
2037 (!IsFlexSmooth(pp
) || RealNear(pp
->nextcp
.x
,pp
->me
.x
)) &&
2038 (!IsFlexSmooth(np
) || RealNear(np
->prevcp
.x
,np
->me
.x
)) &&
2039 np
->me
.x
-sp
->me
.x
< blueshift
&&
2040 np
->me
.x
-sp
->me
.x
> -blueshift
) {
2041 if ( (np
->me
.x
>sp
->me
.x
&&
2042 np
->prevcp
.x
<=np
->me
.x
&& np
->prevcp
.x
>=sp
->me
.x
&&
2043 pp
->nextcp
.x
<=pp
->me
.x
&& pp
->prevcp
.x
>=sp
->me
.x
) ||
2044 (np
->me
.x
<sp
->me
.x
&&
2045 np
->prevcp
.x
>=np
->me
.x
&& np
->prevcp
.x
<=sp
->me
.x
&&
2046 pp
->nextcp
.x
>=pp
->me
.x
&& pp
->prevcp
.x
<=sp
->me
.x
)) {
2048 val
= np
->me
.x
-sp
->me
.x
;
2051 if ( RealNear(sp
->nextcp
.y
,sp
->me
.y
) &&
2052 RealNear(sp
->prevcp
.y
,sp
->me
.y
) &&
2053 RealNear(np
->me
.y
,pp
->me
.y
) &&
2054 !RealNear(np
->me
.y
,sp
->me
.y
) &&
2055 (!IsFlexSmooth(pp
) || RealNear(pp
->nextcp
.y
,pp
->me
.y
)) &&
2056 (!IsFlexSmooth(np
) || RealNear(np
->prevcp
.y
,np
->me
.y
)) &&
2057 np
->me
.y
-sp
->me
.y
< blueshift
&&
2058 np
->me
.y
-sp
->me
.y
> -blueshift
) {
2059 if ( (np
->me
.y
>sp
->me
.y
&&
2060 np
->prevcp
.y
<=np
->me
.y
&& np
->prevcp
.y
>=sp
->me
.y
&&
2061 pp
->nextcp
.y
<=pp
->me
.y
&& pp
->nextcp
.y
>=sp
->me
.y
) ||
2062 (np
->me
.y
<sp
->me
.y
&&
2063 np
->prevcp
.y
>=np
->me
.y
&& np
->prevcp
.y
<=sp
->me
.y
&&
2064 pp
->nextcp
.y
>=pp
->me
.y
&& pp
->nextcp
.y
<=sp
->me
.y
)) {
2066 val
= np
->me
.y
-sp
->me
.y
;
2069 if ( val
<0 ) val
= -val
;
2070 if ( val
>max
) max
= val
;
2073 } while ( sp
!=spl
->first
);
2075 sc
->layers
[layer
].anyflexes
= max
>0;
2077 for ( r
= sc
->layers
[layer
].refs
; r
!=NULL
; r
=r
->next
)
2078 if ( r
->sc
->layers
[layer
].anyflexes
) {
2079 sc
->layers
[layer
].anyflexes
= true;
2087 static void SCUnflex(SplineChar
*sc
, int layer
) {
2091 for ( spl
= sc
->layers
[layer
].splines
; spl
!=NULL
; spl
=spl
->next
) {
2092 /* Mark everything on the path as inflexible */
2095 sp
->flexx
= sp
->flexy
= false;
2096 if ( sp
->next
==NULL
)
2099 if ( sp
==spl
->first
)
2103 sc
->layers
[layer
].anyflexes
= false;
2106 static void FlexDependents(SplineChar
*sc
,int layer
) {
2107 struct splinecharlist
*scl
;
2109 sc
->layers
[layer
].anyflexes
= true;
2110 for ( scl
= sc
->dependents
; scl
!=NULL
; scl
=scl
->next
)
2111 FlexDependents(scl
->sc
,layer
);
2114 int SplineFontIsFlexible(SplineFont
*sf
,int layer
, int flags
) {
2119 /* if the return value is bigger than 6 and we don't have a BlueShift */
2120 /* then we must set BlueShift to ret+1 before saving private dictionary */
2121 /* If the first point in a spline set is flexible, then we must rotate */
2124 if ( flags
&(ps_flag_nohints
|ps_flag_noflex
)) {
2125 for ( i
=0; i
<sf
->glyphcnt
; ++i
) if ( sf
->glyphs
[i
]!=NULL
)
2126 SCUnflex(sf
->glyphs
[i
],layer
);
2130 pt
= PSDictHasEntry(sf
->private,"BlueShift");
2131 blueshift
= 21; /* maximum posible flex, not default */
2133 blueshift
= strtol(pt
,NULL
,10);
2134 if ( blueshift
>21 ) blueshift
= 21;
2135 } else if ( PSDictHasEntry(sf
->private,"BlueValues")!=NULL
)
2136 blueshift
= 7; /* The BlueValues array may depend on BlueShift having its default value */
2138 for ( i
=0; i
<sf
->glyphcnt
; ++i
)
2139 if ( sf
->glyphs
[i
]!=NULL
) if ( sf
->glyphs
[i
]!=NULL
) {
2140 val
= _SplineCharIsFlexible(sf
->glyphs
[i
],layer
,blueshift
);
2141 if ( val
>max
) max
= val
;
2142 if ( sf
->glyphs
[i
]->layers
[layer
].anyflexes
)
2143 FlexDependents(sf
->glyphs
[i
],layer
);