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