1 /*
2 * see COPYRIGHT
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <time.h>
12 #include <ctype.h>
13 #include <math.h>
14
15 #ifndef WINDOWS
16 # include <netinet/in.h>
17 # include <unistd.h>
18 #else
19 # include "windows.h"
20 #endif
21
22 #include "ttf.h"
23 #include "pt1.h"
24 #include "global.h"
25
26 /* big and small values for comparisons */
27 #define FBIGVAL (1e20)
28 #define FEPS (100000./FBIGVAL)
29
30 /* names of the axes */
31 #define X 0
32 #define Y 1
33
34 /* the GENTRY extension structure used in fforceconcise() */
35
36 struct gex_con {
37 double d[2 /*X, Y*/]; /* sizes of curve */
38 double sin2; /* squared sinus of the angle to the next gentry */
39 double len2; /* squared distance between the endpoints */
40
41 /* number of reference dots taken from each curve */
42 #define NREFDOTS 3
43
44 double dots[NREFDOTS][2]; /* reference dots */
45
46 int flags; /* flags for gentry and tits joint to the next gentry */
47 /* a vertical or horizontal line may be in 2 quadrants at once */
48 #define GEXF_QUL 0x00000001 /* in up-left quadrant */
49 #define GEXF_QUR 0x00000002 /* in up-right quadrant */
50 #define GEXF_QDR 0x00000004 /* in down-right quadrant */
51 #define GEXF_QDL 0x00000008 /* in down-left quadrant */
52 #define GEXF_QMASK 0x0000000F /* quadrant mask */
53
54 /* if a line is nearly vertical or horizontal, we remember that idealized quartant too */
55 #define GEXF_QTO_IDEAL(f) (((f)&0xF)<<4)
56 #define GEXF_QFROM_IDEAL(f) (((f)&0xF0)>>4)
57 #define GEXF_IDQ_L 0x00000090 /* left */
58 #define GEXF_IDQ_R 0x00000060 /* right */
59 #define GEXF_IDQ_U 0x00000030 /* up */
60 #define GEXF_IDQ_D 0x000000C0 /* down */
61
62 /* possibly can be joined with conditions:
63 * (in order of increasing preference, the numeric order is important)
64 */
65 #define GEXF_JLINE 0x00000100 /* into one line */
66 #define GEXF_JIGN 0x00000200 /* if one entry's tangent angle is ignored */
67 #define GEXF_JID 0x00000400 /* if one entry is idealized to hor/vert */
68 #define GEXF_JFLAT 0x00000800 /* if one entry is flattened */
69 #define GEXF_JGOOD 0x00001000 /* perfectly, no additional maodifications */
70
71 #define GEXF_JMASK 0x00001F00 /* the mask of all above */
72 #define GEXF_JCVMASK 0x00001E00 /* the mask of all above except JLINE */
73
74 /* which entry needs to be modified for conditional joining */
75 #define GEXF_JIGN1 0x00002000
76 #define GEXF_JIGN2 0x00004000
77 #define GEXF_JIGNDIR(dir) (GEXF_JIGN1<<(dir))
78 #define GEXF_JID1 0x00008000
79 #define GEXF_JID2 0x00010000
80 #define GEXF_JIDDIR(dir) (GEXF_JID1<<(dir))
81 #define GEXF_JFLAT1 0x00020000
82 #define GEXF_JFLAT2 0x00040000
83 #define GEXF_JFLATDIR(dir) (GEXF_JFLAT1<<(dir))
84
85 #define GEXF_VERT 0x00100000 /* is nearly vertical */
86 #define GEXF_HOR 0x00200000 /* is nearly horizontal */
87 #define GEXF_FLAT 0x00400000 /* is nearly flat */
88
89 #define GEXF_VDOTS 0x01000000 /* the dots are valid */
90
91 signed char isd[2 /*X,Y*/]; /* signs of the sizes */
92 };
93 typedef struct gex_con GEX_CON;
94
95 /* convenience macros */
96 #define X_CON(ge) ((GEX_CON *)((ge)->ext))
97 #define X_CON_D(ge) (X_CON(ge)->d)
98 #define X_CON_DX(ge) (X_CON(ge)->d[0])
99 #define X_CON_DY(ge) (X_CON(ge)->d[1])
100 #define X_CON_ISD(ge) (X_CON(ge)->isd)
101 #define X_CON_ISDX(ge) (X_CON(ge)->isd[0])
102 #define X_CON_ISDY(ge) (X_CON(ge)->isd[1])
103 #define X_CON_SIN2(ge) (X_CON(ge)->sin2)
104 #define X_CON_LEN2(ge) (X_CON(ge)->len2)
105 #define X_CON_F(ge) (X_CON(ge)->flags)
106
107 /* performance statistics about guessing the concise curves */
108 static int ggoodcv=0, ggoodcvdots=0, gbadcv=0, gbadcvdots=0;
109
110 int stdhw, stdvw; /* dominant stems widths */
111 int stemsnaph[12], stemsnapv[12]; /* most typical stem width */
112
113 int bluevalues[14];
114 int nblues;
115 int otherblues[10];
116 int notherb;
117 int bbox[4]; /* the FontBBox array */
118 double italic_angle;
119
120 GLYPH *glyph_list;
121 int encoding[ENCTABSZ]; /* inverse of glyph[].char_no */
122 int kerning_pairs = 0;
123
124 /* prototypes */
125 static void fixcvdir( GENTRY * ge, int dir);
126 static void fixcvends( GENTRY * ge);
127 static int fgetcvdir( GENTRY * ge);
128 static int igetcvdir( GENTRY * ge);
129 static int fiszigzag( GENTRY *ge);
130 static int iiszigzag( GENTRY *ge);
131 static GENTRY * freethisge( GENTRY *ge);
132 static void addgeafter( GENTRY *oge, GENTRY *nge );
133 static GENTRY * newgentry( int flags);
134 static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs);
135 static int addbluestems( STEM *s, int n);
136 static void sortstems( STEM * s, int n);
137 static int stemoverlap( STEM * s1, STEM * s2);
138 static int steminblue( STEM *s);
139 static void markbluestems( STEM *s, int nold);
140 static int joinmainstems( STEM * s, int nold, int useblues);
141 static void joinsubstems( STEM * s, short *pairs, int nold, int useblues);
142 static void fixendpath( GENTRY *ge);
143 static void fdelsmall( GLYPH *g, double minlen);
144 static void alloc_gex_con( GENTRY *ge);
145 static double fjointsin2( GENTRY *ge1, GENTRY *ge2);
146 static double fcvarea( GENTRY *ge);
147 static double fcvval( GENTRY *ge, int axis, double t);
148 static void fsampledots( GENTRY *ge, double dots[][2], int ndots);
149 static void fnormalizege( GENTRY *ge);
150 static void fanalyzege( GENTRY *ge);
151 static void fanalyzejoint( GENTRY *ge);
152 static void fconcisecontour( GLYPH *g, GENTRY *ge);
153 static double fclosegap( GENTRY *from, GENTRY *to, int axis,
154 double gap, double *ret);
155
156 int
isign(int x)157 isign(
158 int x
159 )
160 {
161 if (x > 0)
162 return 1;
163 else if (x < 0)
164 return -1;
165 else
166 return 0;
167 }
168
169 int
fsign(double x)170 fsign(
171 double x
172 )
173 {
174 if (x > 0.0)
175 return 1;
176 else if (x < 0.0)
177 return -1;
178 else
179 return 0;
180 }
181
182 static GENTRY *
newgentry(int flags)183 newgentry(
184 int flags
185 )
186 {
187 GENTRY *ge;
188
189 ge = calloc(1, sizeof(GENTRY));
190
191 if (ge == 0) {
192 fprintf(stderr, "***** Memory allocation error *****\n");
193 exit(255);
194 }
195 ge->stemid = -1;
196 ge->flags = flags;
197 /* the rest is set to 0 by calloc() */
198 return ge;
199 }
200
201 /*
202 * Routines to print out Postscript functions with optimization
203 */
204
205 void
rmoveto(int dx,int dy)206 rmoveto(
207 int dx,
208 int dy
209 )
210 {
211 if (optimize && dx == 0)
212 fprintf(pfa_file, "%d vmoveto\n", dy);
213 else if (optimize && dy == 0)
214 fprintf(pfa_file, "%d hmoveto\n", dx);
215 else
216 fprintf(pfa_file, "%d %d rmoveto\n", dx, dy);
217 }
218
219 void
rlineto(int dx,int dy)220 rlineto(
221 int dx,
222 int dy
223 )
224 {
225 if (optimize && dx == 0 && dy == 0) /* for special pathologic
226 * case */
227 return;
228 else if (optimize && dx == 0)
229 fprintf(pfa_file, "%d vlineto\n", dy);
230 else if (optimize && dy == 0)
231 fprintf(pfa_file, "%d hlineto\n", dx);
232 else
233 fprintf(pfa_file, "%d %d rlineto\n", dx, dy);
234 }
235
236 void
rrcurveto(int dx1,int dy1,int dx2,int dy2,int dx3,int dy3)237 rrcurveto(
238 int dx1,
239 int dy1,
240 int dx2,
241 int dy2,
242 int dx3,
243 int dy3
244 )
245 {
246 /* first two ifs are for crazy cases that occur surprisingly often */
247 if (optimize && dx1 == 0 && dx2 == 0 && dx3 == 0)
248 rlineto(0, dy1 + dy2 + dy3);
249 else if (optimize && dy1 == 0 && dy2 == 0 && dy3 == 0)
250 rlineto(dx1 + dx2 + dx3, 0);
251 else if (optimize && dy1 == 0 && dx3 == 0)
252 fprintf(pfa_file, "%d %d %d %d hvcurveto\n",
253 dx1, dx2, dy2, dy3);
254 else if (optimize && dx1 == 0 && dy3 == 0)
255 fprintf(pfa_file, "%d %d %d %d vhcurveto\n",
256 dy1, dx2, dy2, dx3);
257 else
258 fprintf(pfa_file, "%d %d %d %d %d %d rrcurveto\n",
259 dx1, dy1, dx2, dy2, dx3, dy3);
260 }
261
262 void
closepath(void)263 closepath(void)
264 {
265 fprintf(pfa_file, "closepath\n");
266 }
267
268 /*
269 * Many of the path processing routines exist (or will exist) in
270 * both floating-point and integer version. Fimally most of the
271 * processing will go in floating point and the integer processing
272 * will become legacy.
273 * The names of floating routines start with f, names of integer
274 * routines start with i, and those old routines existing in one
275 * version only have no such prefix at all.
276 */
277
278 /*
279 ** Routine that checks integrity of the path, for debugging
280 */
281
282 void
assertpath(GENTRY * from,char * file,int line,char * name)283 assertpath(
284 GENTRY * from,
285 char *file,
286 int line,
287 char *name
288 )
289 {
290 GENTRY *first, *pe, *ge;
291 int isfloat;
292
293 if(from==0)
294 return;
295 isfloat = (from->flags & GEF_FLOAT);
296 pe = from->prev;
297 for (ge = from; ge != 0; pe = ge, ge = ge->next) {
298 if( (ge->flags & GEF_FLOAT) ^ isfloat ) {
299 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
300 fprintf(stderr, "float flag changes from %s to %s at 0x%p (type %c, prev type %c)\n",
301 (isfloat ? "TRUE" : "FALSE"), (isfloat ? "FALSE" : "TRUE"), ge, ge->type, pe->type);
302 abort();
303 }
304 if (pe != ge->prev) {
305 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
306 fprintf(stderr, "unidirectional chain 0x%x -next-> 0x%x -prev-> 0x%x \n",
307 pe, ge, ge->prev);
308 abort();
309 }
310
311 switch(ge->type) {
312 case GE_MOVE:
313 break;
314 case GE_PATH:
315 if (ge->prev == 0) {
316 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
317 fprintf(stderr, "empty path at 0x%x \n", ge);
318 abort();
319 }
320 break;
321 case GE_LINE:
322 case GE_CURVE:
323 if(ge->frwd->bkwd != ge) {
324 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
325 fprintf(stderr, "unidirectional chain 0x%x -frwd-> 0x%x -bkwd-> 0x%x \n",
326 ge, ge->frwd, ge->frwd->bkwd);
327 abort();
328 }
329 if(ge->prev->type == GE_MOVE) {
330 first = ge;
331 if(ge->bkwd->next->type != GE_PATH) {
332 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
333 fprintf(stderr, "broken first backlink 0x%x -bkwd-> 0x%x -next-> 0x%x \n",
334 ge, ge->bkwd, ge->bkwd->next);
335 abort();
336 }
337 }
338 if(ge->next->type == GE_PATH) {
339 if(ge->frwd != first) {
340 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
341 fprintf(stderr, "broken loop 0x%x -...-> 0x%x -frwd-> 0x%x \n",
342 first, ge, ge->frwd);
343 abort();
344 }
345 }
346 break;
347 }
348
349 }
350 }
351
352 void
assertisfloat(GLYPH * g,char * msg)353 assertisfloat(
354 GLYPH *g,
355 char *msg
356 )
357 {
358 if( !(g->flags & GF_FLOAT) ) {
359 fprintf(stderr, "**! Glyph %s is not float: %s\n", g->name, msg);
360 abort();
361 }
362 if(g->lastentry) {
363 if( !(g->lastentry->flags & GEF_FLOAT) ) {
364 fprintf(stderr, "**! Glyphs %s last entry is int: %s\n", g->name, msg);
365 abort();
366 }
367 }
368 }
369
370 void
assertisint(GLYPH * g,char * msg)371 assertisint(
372 GLYPH *g,
373 char *msg
374 )
375 {
376 if( (g->flags & GF_FLOAT) ) {
377 fprintf(stderr, "**! Glyph %s is not int: %s\n", g->name, msg);
378 abort();
379 }
380 if(g->lastentry) {
381 if( (g->lastentry->flags & GEF_FLOAT) ) {
382 fprintf(stderr, "**! Glyphs %s last entry is float: %s\n", g->name, msg);
383 abort();
384 }
385 }
386 }
387
388
389 /*
390 * Routines to save the generated data about glyph
391 */
392
393 void
fg_rmoveto(GLYPH * g,double x,double y)394 fg_rmoveto(
395 GLYPH * g,
396 double x,
397 double y)
398 {
399 GENTRY *oge;
400
401 if (ISDBG(BUILDG))
402 fprintf(stderr, "%s: f rmoveto(%g, %g)\n", g->name, x, y);
403
404 assertisfloat(g, "adding float MOVE");
405
406 if ((oge = g->lastentry) != 0) {
407 if (oge->type == GE_MOVE) { /* just eat up the first move */
408 oge->fx3 = x;
409 oge->fy3 = y;
410 } else if (oge->type == GE_LINE || oge->type == GE_CURVE) {
411 fprintf(stderr, "Glyph %s: MOVE in middle of path\n", g->name);
412 } else {
413 GENTRY *nge;
414
415 nge = newgentry(GEF_FLOAT);
416 nge->type = GE_MOVE;
417 nge->fx3 = x;
418 nge->fy3 = y;
419
420 oge->next = nge;
421 nge->prev = oge;
422 g->lastentry = nge;
423 }
424 } else {
425 GENTRY *nge;
426
427 nge = newgentry(GEF_FLOAT);
428 nge->type = GE_MOVE;
429 nge->fx3 = x;
430 nge->fy3 = y;
431 nge->bkwd = (GENTRY*)&g->entries;
432 g->entries = g->lastentry = nge;
433 }
434
435 if (0 && ISDBG(BUILDG))
436 dumppaths(g, NULL, NULL);
437 }
438
439 void
ig_rmoveto(GLYPH * g,int x,int y)440 ig_rmoveto(
441 GLYPH * g,
442 int x,
443 int y)
444 {
445 GENTRY *oge;
446
447 if (ISDBG(BUILDG))
448 fprintf(stderr, "%s: i rmoveto(%d, %d)\n", g->name, x, y);
449
450 assertisint(g, "adding int MOVE");
451
452 if ((oge = g->lastentry) != 0) {
453 if (oge->type == GE_MOVE) { /* just eat up the first move */
454 oge->ix3 = x;
455 oge->iy3 = y;
456 } else if (oge->type == GE_LINE || oge->type == GE_CURVE) {
457 fprintf(stderr, "Glyph %s: MOVE in middle of path, ignored\n", g->name);
458 } else {
459 GENTRY *nge;
460
461 nge = newgentry(0);
462 nge->type = GE_MOVE;
463 nge->ix3 = x;
464 nge->iy3 = y;
465
466 oge->next = nge;
467 nge->prev = oge;
468 g->lastentry = nge;
469 }
470 } else {
471 GENTRY *nge;
472
473 nge = newgentry(0);
474 nge->type = GE_MOVE;
475 nge->ix3 = x;
476 nge->iy3 = y;
477 nge->bkwd = (GENTRY*)&g->entries;
478 g->entries = g->lastentry = nge;
479 }
480
481 }
482
483 void
fg_rlineto(GLYPH * g,double x,double y)484 fg_rlineto(
485 GLYPH * g,
486 double x,
487 double y)
488 {
489 GENTRY *oge, *nge;
490
491 if (ISDBG(BUILDG))
492 fprintf(stderr, "%s: f rlineto(%g, %g)\n", g->name, x, y);
493
494 assertisfloat(g, "adding float LINE");
495
496 nge = newgentry(GEF_FLOAT);
497 nge->type = GE_LINE;
498 nge->fx3 = x;
499 nge->fy3 = y;
500
501 if ((oge = g->lastentry) != 0) {
502 if (x == oge->fx3 && y == oge->fy3) { /* empty line */
503 /* ignore it or we will get in troubles later */
504 free(nge);
505 return;
506 }
507 if (g->path == 0) {
508 g->path = nge;
509 nge->bkwd = nge->frwd = nge;
510 } else {
511 oge->frwd = nge;
512 nge->bkwd = oge;
513 g->path->bkwd = nge;
514 nge->frwd = g->path;
515 }
516
517 oge->next = nge;
518 nge->prev = oge;
519 g->lastentry = nge;
520 } else {
521 WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name);
522 free(nge);
523 }
524
525 if (0 && ISDBG(BUILDG))
526 dumppaths(g, NULL, NULL);
527 }
528
529 void
ig_rlineto(GLYPH * g,int x,int y)530 ig_rlineto(
531 GLYPH * g,
532 int x,
533 int y)
534 {
535 GENTRY *oge, *nge;
536
537 if (ISDBG(BUILDG))
538 fprintf(stderr, "%s: i rlineto(%d, %d)\n", g->name, x, y);
539
540 assertisint(g, "adding int LINE");
541
542 nge = newgentry(0);
543 nge->type = GE_LINE;
544 nge->ix3 = x;
545 nge->iy3 = y;
546
547 if ((oge = g->lastentry) != 0) {
548 if (x == oge->ix3 && y == oge->iy3) { /* empty line */
549 /* ignore it or we will get in troubles later */
550 free(nge);
551 return;
552 }
553 if (g->path == 0) {
554 g->path = nge;
555 nge->bkwd = nge->frwd = nge;
556 } else {
557 oge->frwd = nge;
558 nge->bkwd = oge;
559 g->path->bkwd = nge;
560 nge->frwd = g->path;
561 }
562
563 oge->next = nge;
564 nge->prev = oge;
565 g->lastentry = nge;
566 } else {
567 WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name);
568 free(nge);
569 }
570
571 }
572
573 void
fg_rrcurveto(GLYPH * g,double x1,double y1,double x2,double y2,double x3,double y3)574 fg_rrcurveto(
575 GLYPH * g,
576 double x1,
577 double y1,
578 double x2,
579 double y2,
580 double x3,
581 double y3)
582 {
583 GENTRY *oge, *nge;
584
585 oge = g->lastentry;
586
587 if (ISDBG(BUILDG))
588 fprintf(stderr, "%s: f rrcurveto(%g, %g, %g, %g, %g, %g)\n"
589 ,g->name, x1, y1, x2, y2, x3, y3);
590
591 assertisfloat(g, "adding float CURVE");
592
593 if (oge && oge->fx3 == x1 && x1 == x2 && x2 == x3) /* check if it's
594 * actually a line */
595 fg_rlineto(g, x1, y3);
596 else if (oge && oge->fy3 == y1 && y1 == y2 && y2 == y3)
597 fg_rlineto(g, x3, y1);
598 else {
599 nge = newgentry(GEF_FLOAT);
600 nge->type = GE_CURVE;
601 nge->fx1 = x1;
602 nge->fy1 = y1;
603 nge->fx2 = x2;
604 nge->fy2 = y2;
605 nge->fx3 = x3;
606 nge->fy3 = y3;
607
608 if (oge != 0) {
609 if (x3 == oge->fx3 && y3 == oge->fy3) {
610 free(nge); /* consider this curve empty */
611 /* ignore it or we will get in troubles later */
612 return;
613 }
614 if (g->path == 0) {
615 g->path = nge;
616 nge->bkwd = nge->frwd = nge;
617 } else {
618 oge->frwd = nge;
619 nge->bkwd = oge;
620 g->path->bkwd = nge;
621 nge->frwd = g->path;
622 }
623
624 oge->next = nge;
625 nge->prev = oge;
626 g->lastentry = nge;
627 } else {
628 WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name);
629 free(nge);
630 }
631 }
632
633 if (0 && ISDBG(BUILDG))
634 dumppaths(g, NULL, NULL);
635 }
636
637 void
ig_rrcurveto(GLYPH * g,int x1,int y1,int x2,int y2,int x3,int y3)638 ig_rrcurveto(
639 GLYPH * g,
640 int x1,
641 int y1,
642 int x2,
643 int y2,
644 int x3,
645 int y3)
646 {
647 GENTRY *oge, *nge;
648
649 oge = g->lastentry;
650
651 if (ISDBG(BUILDG))
652 fprintf(stderr, "%s: i rrcurveto(%d, %d, %d, %d, %d, %d)\n"
653 ,g->name, x1, y1, x2, y2, x3, y3);
654
655 assertisint(g, "adding int CURVE");
656
657 if (oge && oge->ix3 == x1 && x1 == x2 && x2 == x3) /* check if it's
658 * actually a line */
659 ig_rlineto(g, x1, y3);
660 else if (oge && oge->iy3 == y1 && y1 == y2 && y2 == y3)
661 ig_rlineto(g, x3, y1);
662 else {
663 nge = newgentry(0);
664 nge->type = GE_CURVE;
665 nge->ix1 = x1;
666 nge->iy1 = y1;
667 nge->ix2 = x2;
668 nge->iy2 = y2;
669 nge->ix3 = x3;
670 nge->iy3 = y3;
671
672 if (oge != 0) {
673 if (x3 == oge->ix3 && y3 == oge->iy3) {
674 free(nge); /* consider this curve empty */
675 /* ignore it or we will get in troubles later */
676 return;
677 }
678 if (g->path == 0) {
679 g->path = nge;
680 nge->bkwd = nge->frwd = nge;
681 } else {
682 oge->frwd = nge;
683 nge->bkwd = oge;
684 g->path->bkwd = nge;
685 nge->frwd = g->path;
686 }
687
688 oge->next = nge;
689 nge->prev = oge;
690 g->lastentry = nge;
691 } else {
692 WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name);
693 free(nge);
694 }
695 }
696 }
697
698 void
g_closepath(GLYPH * g)699 g_closepath(
700 GLYPH * g
701 )
702 {
703 GENTRY *oge, *nge;
704
705 if (ISDBG(BUILDG))
706 fprintf(stderr, "%s: closepath\n", g->name);
707
708 oge = g->lastentry;
709
710 if (g->path == 0) {
711 WARNING_1 fprintf(stderr, "Warning: **** closepath on empty path in glyph \"%s\" ****\n",
712 g->name);
713 if (oge == 0) {
714 WARNING_1 fprintf(stderr, "No previois entry\n");
715 } else {
716 WARNING_1 fprintf(stderr, "Previous entry type: %c\n", oge->type);
717 if (oge->type == GE_MOVE) {
718 g->lastentry = oge->prev;
719 if (oge->prev == 0)
720 g->entries = 0;
721 else
722 g->lastentry->next = 0;
723 free(oge);
724 }
725 }
726 return;
727 }
728
729 nge = newgentry(oge->flags & GEF_FLOAT); /* keep the same type */
730 nge->type = GE_PATH;
731
732 g->path = 0;
733
734 oge->next = nge;
735 nge->prev = oge;
736 g->lastentry = nge;
737
738 if (0 && ISDBG(BUILDG))
739 dumppaths(g, NULL, NULL);
740 }
741
742 /*
743 * * SB * Routines to smooth and fix the glyphs
744 */
745
746 /*
747 ** we don't want to see the curves with coinciding middle and
748 ** outer points
749 */
750
751 static void
fixcvends(GENTRY * ge)752 fixcvends(
753 GENTRY * ge
754 )
755 {
756 int dx, dy;
757 int x0, y0, x1, y1, x2, y2, x3, y3;
758
759 if (ge->type != GE_CURVE)
760 return;
761
762 if(ge->flags & GEF_FLOAT) {
763 fprintf(stderr, "**! fixcvends(0x%x) on floating entry, ABORT\n", ge);
764 abort(); /* dump core */
765 }
766
767 x0 = ge->prev->ix3;
768 y0 = ge->prev->iy3;
769 x1 = ge->ix1;
770 y1 = ge->iy1;
771 x2 = ge->ix2;
772 y2 = ge->iy2;
773 x3 = ge->ix3;
774 y3 = ge->iy3;
775
776
777 /* look at the start of the curve */
778 if (x1 == x0 && y1 == y0) {
779 dx = x2 - x1;
780 dy = y2 - y1;
781
782 if (dx == 0 && dy == 0
783 || x2 == x3 && y2 == y3) {
784 /* Oops, we actually have a straight line */
785 /*
786 * if it's small, we hope that it will get optimized
787 * later
788 */
789 if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) {
790 ge->ix1 = x3;
791 ge->iy1 = y3;
792 ge->ix2 = x0;
793 ge->iy2 = y0;
794 } else {/* just make it a line */
795 ge->type = GE_LINE;
796 }
797 } else {
798 if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very
799 * small */
800 ge->ix1 = x2;
801 ge->iy1 = y2;
802 } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */
803 ge->ix1 += dx / 2;
804 ge->iy1 += dy / 2;
805 } else {
806 ge->ix1 += dx / 4;
807 ge->iy1 += dy / 4;
808 }
809 /* make sure that it's still on the same side */
810 if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) {
811 if (abs(x3 - x0) * abs(ge->iy1 - y0) > abs(y3 - y0) * abs(ge->ix1 - x0))
812 ge->ix1 += isign(dx);
813 } else {
814 if (abs(x3 - x0) * abs(ge->iy1 - y0) < abs(y3 - y0) * abs(ge->ix1 - x0))
815 ge->iy1 += isign(dy);
816 }
817
818 ge->ix2 += (x3 - x2) / 8;
819 ge->iy2 += (y3 - y2) / 8;
820 /* make sure that it's still on the same side */
821 if (abs(x3 - x0) * abs(y3 - y2) < abs(y3 - y0) * abs(x3 - x2)) {
822 if (abs(x3 - x0) * abs(y3 - ge->iy2) > abs(y3 - y0) * abs(x3 - ge->ix2))
823 ge->iy1 -= isign(y3 - y2);
824 } else {
825 if (abs(x3 - x0) * abs(y3 - ge->iy2) < abs(y3 - y0) * abs(x3 - ge->ix2))
826 ge->ix1 -= isign(x3 - x2);
827 }
828
829 }
830 } else if (x2 == x3 && y2 == y3) {
831 dx = x1 - x2;
832 dy = y1 - y2;
833
834 if (dx == 0 && dy == 0) {
835 /* Oops, we actually have a straight line */
836 /*
837 * if it's small, we hope that it will get optimized
838 * later
839 */
840 if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) {
841 ge->ix1 = x3;
842 ge->iy1 = y3;
843 ge->ix2 = x0;
844 ge->iy2 = y0;
845 } else {/* just make it a line */
846 ge->type = GE_LINE;
847 }
848 } else {
849 if (abs(dx) < 4 && abs(dy) < 4) { /* consider it very
850 * small */
851 ge->ix2 = x1;
852 ge->iy2 = y1;
853 } else if (abs(dx) < 8 && abs(dy) < 8) { /* consider it small */
854 ge->ix2 += dx / 2;
855 ge->iy2 += dy / 2;
856 } else {
857 ge->ix2 += dx / 4;
858 ge->iy2 += dy / 4;
859 }
860 /* make sure that it's still on the same side */
861 if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) {
862 if (abs(x3 - x0) * abs(ge->iy2 - y3) > abs(y3 - y0) * abs(ge->ix2 - x3))
863 ge->ix2 += isign(dx);
864 } else {
865 if (abs(x3 - x0) * abs(ge->iy2 - y3) < abs(y3 - y0) * abs(ge->ix2 - x3))
866 ge->iy2 += isign(dy);
867 }
868
869 ge->ix1 += (x0 - x1) / 8;
870 ge->iy1 += (y0 - y1) / 8;
871 /* make sure that it's still on the same side */
872 if (abs(x3 - x0) * abs(y0 - y1) < abs(y3 - y0) * abs(x0 - x1)) {
873 if (abs(x3 - x0) * abs(y0 - ge->iy1) > abs(y3 - y0) * abs(x0 - ge->ix1))
874 ge->iy1 -= isign(y0 - y1);
875 } else {
876 if (abs(x3 - x0) * abs(y0 - ge->iy1) < abs(y3 - y0) * abs(x0 - ge->ix1))
877 ge->ix1 -= isign(x0 - x1);
878 }
879
880 }
881 }
882 }
883
884 /*
885 ** After transformations we want to make sure that the resulting
886 ** curve is going in the same quadrant as the original one,
887 ** because rounding errors introduced during transformations
888 ** may make the result completeley wrong.
889 **
890 ** `dir' argument describes the direction of the original curve,
891 ** it is the superposition of two values for the front and
892 ** rear ends of curve:
893 **
894 ** >EQUAL - goes over the line connecting the ends
895 ** =EQUAL - coincides with the line connecting the ends
896 ** <EQUAL - goes under the line connecting the ends
897 **
898 ** See CVDIR_* for exact definitions.
899 */
900
901 static void
fixcvdir(GENTRY * ge,int dir)902 fixcvdir(
903 GENTRY * ge,
904 int dir
905 )
906 {
907 int a, b, c, d;
908 double kk, kk1, kk2;
909 int changed;
910 int fdir, rdir;
911
912 if(ge->flags & GEF_FLOAT) {
913 fprintf(stderr, "**! fixcvdir(0x%x) on floating entry, ABORT\n", ge);
914 abort(); /* dump core */
915 }
916
917 fdir = (dir & CVDIR_FRONT) - CVDIR_FEQUAL;
918 if ((dir & CVDIR_REAR) == CVDIR_RSAME)
919 rdir = fdir; /* we need only isign, exact value doesn't matter */
920 else
921 rdir = (dir & CVDIR_REAR) - CVDIR_REQUAL;
922
923 fixcvends(ge);
924
925 c = isign(ge->ix3 - ge->prev->ix3); /* note the direction of
926 * curve */
927 d = isign(ge->iy3 - ge->prev->iy3);
928
929 a = ge->iy3 - ge->prev->iy3;
930 b = ge->ix3 - ge->prev->ix3;
931 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
932 a = ge->iy1 - ge->prev->iy3;
933 b = ge->ix1 - ge->prev->ix3;
934 kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
935 a = ge->iy3 - ge->iy2;
936 b = ge->ix3 - ge->ix2;
937 kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
938
939 changed = 1;
940 while (changed) {
941 if (ISDBG(FIXCVDIR)) {
942 /* for debugging */
943 fprintf(stderr, "fixcvdir %d %d (%d %d %d %d %d %d) %f %f %f\n",
944 fdir, rdir,
945 ge->ix1 - ge->prev->ix3,
946 ge->iy1 - ge->prev->iy3,
947 ge->ix2 - ge->ix1,
948 ge->iy2 - ge->iy1,
949 ge->ix3 - ge->ix2,
950 ge->iy3 - ge->iy2,
951 kk1, kk, kk2);
952 }
953 changed = 0;
954
955 if (fdir > 0) {
956 if (kk1 > kk) { /* the front end has problems */
957 if (c * (ge->ix1 - ge->prev->ix3) > 0) {
958 ge->ix1 -= c;
959 changed = 1;
960 } if (d * (ge->iy2 - ge->iy1) > 0) {
961 ge->iy1 += d;
962 changed = 1;
963 }
964 /* recalculate the coefficients */
965 a = ge->iy3 - ge->prev->iy3;
966 b = ge->ix3 - ge->prev->ix3;
967 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
968 a = ge->iy1 - ge->prev->iy3;
969 b = ge->ix1 - ge->prev->ix3;
970 kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
971 }
972 } else if (fdir < 0) {
973 if (kk1 < kk) { /* the front end has problems */
974 if (c * (ge->ix2 - ge->ix1) > 0) {
975 ge->ix1 += c;
976 changed = 1;
977 } if (d * (ge->iy1 - ge->prev->iy3) > 0) {
978 ge->iy1 -= d;
979 changed = 1;
980 }
981 /* recalculate the coefficients */
982 a = ge->iy1 - ge->prev->iy3;
983 b = ge->ix1 - ge->prev->ix3;
984 kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
985 a = ge->iy3 - ge->prev->iy3;
986 b = ge->ix3 - ge->prev->ix3;
987 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
988 }
989 }
990 if (rdir > 0) {
991 if (kk2 < kk) { /* the rear end has problems */
992 if (c * (ge->ix2 - ge->ix1) > 0) {
993 ge->ix2 -= c;
994 changed = 1;
995 } if (d * (ge->iy3 - ge->iy2) > 0) {
996 ge->iy2 += d;
997 changed = 1;
998 }
999 /* recalculate the coefficients */
1000 a = ge->iy3 - ge->prev->iy3;
1001 b = ge->ix3 - ge->prev->ix3;
1002 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
1003 a = ge->iy3 - ge->iy2;
1004 b = ge->ix3 - ge->ix2;
1005 kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
1006 }
1007 } else if (rdir < 0) {
1008 if (kk2 > kk) { /* the rear end has problems */
1009 if (c * (ge->ix3 - ge->ix2) > 0) {
1010 ge->ix2 += c;
1011 changed = 1;
1012 } if (d * (ge->iy2 - ge->iy1) > 0) {
1013 ge->iy2 -= d;
1014 changed = 1;
1015 }
1016 /* recalculate the coefficients */
1017 a = ge->iy3 - ge->prev->iy3;
1018 b = ge->ix3 - ge->prev->ix3;
1019 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
1020 a = ge->iy3 - ge->iy2;
1021 b = ge->ix3 - ge->ix2;
1022 kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
1023 }
1024 }
1025 }
1026 fixcvends(ge);
1027 }
1028
1029 /* Get the directions of ends of curve for further usage */
1030
1031 /* expects that the previous element is also float */
1032
1033 static int
fgetcvdir(GENTRY * ge)1034 fgetcvdir(
1035 GENTRY * ge
1036 )
1037 {
1038 double a, b;
1039 double k, k1, k2;
1040 int dir = 0;
1041
1042 if( !(ge->flags & GEF_FLOAT) ) {
1043 fprintf(stderr, "**! fgetcvdir(0x%x) on int entry, ABORT\n", ge);
1044 abort(); /* dump core */
1045 }
1046
1047 a = fabs(ge->fy3 - ge->prev->fy3);
1048 b = fabs(ge->fx3 - ge->prev->fx3);
1049 k = a < FEPS ? (b < FEPS ? 1. : 100000.) : ( b / a);
1050
1051 a = fabs(ge->fy1 - ge->prev->fy3);
1052 b = fabs(ge->fx1 - ge->prev->fx3);
1053 if(a < FEPS) {
1054 if(b < FEPS) {
1055 a = fabs(ge->fy2 - ge->prev->fy3);
1056 b = fabs(ge->fx2 - ge->prev->fx3);
1057 k1 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a);
1058 } else
1059 k1 = FBIGVAL;
1060 } else
1061 k1 = b / a;
1062
1063 a = fabs(ge->fy3 - ge->fy2);
1064 b = fabs(ge->fx3 - ge->fx2);
1065 if(a < FEPS) {
1066 if(b < FEPS) {
1067 a = fabs(ge->fy3 - ge->fy1);
1068 b = fabs(ge->fx3 - ge->fx1);
1069 k2 = a < FEPS ? (b < FEPS ? k : 100000.) : ( b / a);
1070 } else
1071 k2 = FBIGVAL;
1072 } else
1073 k2 = b / a;
1074
1075 if(fabs(k1-k) < 0.0001)
1076 dir |= CVDIR_FEQUAL;
1077 else if (k1 < k)
1078 dir |= CVDIR_FUP;
1079 else
1080 dir |= CVDIR_FDOWN;
1081
1082 if(fabs(k2-k) < 0.0001)
1083 dir |= CVDIR_REQUAL;
1084 else if (k2 > k)
1085 dir |= CVDIR_RUP;
1086 else
1087 dir |= CVDIR_RDOWN;
1088
1089 return dir;
1090 }
1091
1092
1093 /* expects that the previous element is also int */
1094
1095 static int
igetcvdir(GENTRY * ge)1096 igetcvdir(
1097 GENTRY * ge
1098 )
1099 {
1100 int a, b;
1101 double k, k1, k2;
1102 int dir = 0;
1103
1104 if(ge->flags & GEF_FLOAT) {
1105 fprintf(stderr, "**! igetcvdir(0x%x) on floating entry, ABORT\n", ge);
1106 abort(); /* dump core */
1107 }
1108
1109 a = ge->iy3 - ge->prev->iy3;
1110 b = ge->ix3 - ge->prev->ix3;
1111 k = (a == 0) ? (b == 0 ? 1. : 100000.) : fabs((double) b / (double) a);
1112
1113 a = ge->iy1 - ge->prev->iy3;
1114 b = ge->ix1 - ge->prev->ix3;
1115 if(a == 0) {
1116 if(b == 0) {
1117 a = ge->iy2 - ge->prev->iy3;
1118 b = ge->ix2 - ge->prev->ix3;
1119 k1 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a);
1120 } else
1121 k1 = FBIGVAL;
1122 } else
1123 k1 = fabs((double) b / (double) a);
1124
1125 a = ge->iy3 - ge->iy2;
1126 b = ge->ix3 - ge->ix2;
1127 if(a == 0) {
1128 if(b == 0) {
1129 a = ge->iy3 - ge->iy1;
1130 b = ge->ix3 - ge->ix1;
1131 k2 = (a == 0) ? (b == 0 ? k : 100000.) : fabs((double) b / (double) a);
1132 } else
1133 k2 = FBIGVAL;
1134 } else
1135 k2 = fabs((double) b / (double) a);
1136
1137 if(fabs(k1-k) < 0.0001)
1138 dir |= CVDIR_FEQUAL;
1139 else if (k1 < k)
1140 dir |= CVDIR_FUP;
1141 else
1142 dir |= CVDIR_FDOWN;
1143
1144 if(fabs(k2-k) < 0.0001)
1145 dir |= CVDIR_REQUAL;
1146 else if (k2 > k)
1147 dir |= CVDIR_RUP;
1148 else
1149 dir |= CVDIR_RDOWN;
1150
1151 return dir;
1152 }
1153
1154 #if 0
1155 /* a function just to test the work of fixcvdir() */
1156 static void
1157 testfixcvdir(
1158 GLYPH * g
1159 )
1160 {
1161 GENTRY *ge;
1162 int dir;
1163
1164 for (ge = g->entries; ge != 0; ge = ge->next) {
1165 if (ge->type == GE_CURVE) {
1166 dir = igetcvdir(ge);
1167 fixcvdir(ge, dir);
1168 }
1169 }
1170 }
1171 #endif
1172
1173 static int
iround(double val)1174 iround(
1175 double val
1176 )
1177 {
1178 return (int) (val > 0 ? val + 0.5 : val - 0.5);
1179 }
1180
1181 /* for debugging - dump the glyph
1182 * mark with a star the entries from start to end inclusive
1183 * (start == NULL means don't mark any, end == NULL means to the last)
1184 */
1185
1186 void
dumppaths(GLYPH * g,GENTRY * start,GENTRY * end)1187 dumppaths(
1188 GLYPH *g,
1189 GENTRY *start,
1190 GENTRY *end
1191 )
1192 {
1193 GENTRY *ge;
1194 int i;
1195 char mark=' ';
1196
1197 fprintf(stderr, "Glyph %s:\n", g->name);
1198
1199 /* now do the conversion */
1200 for(ge = g->entries; ge != 0; ge = ge->next) {
1201 if(ge == start)
1202 mark = '*';
1203 fprintf(stderr, " %c %8x", mark, ge);
1204 switch(ge->type) {
1205 case GE_MOVE:
1206 case GE_LINE:
1207 if(ge->flags & GEF_FLOAT)
1208 fprintf(stderr," %c float (%g, %g)\n", ge->type, ge->fx3, ge->fy3);
1209 else
1210 fprintf(stderr," %c int (%d, %d)\n", ge->type, ge->ix3, ge->iy3);
1211 break;
1212 case GE_CURVE:
1213 if(ge->flags & GEF_FLOAT) {
1214 fprintf(stderr," C float ");
1215 for(i=0; i<3; i++)
1216 fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]);
1217 fprintf(stderr,"\n");
1218 } else {
1219 fprintf(stderr," C int ");
1220 for(i=0; i<3; i++)
1221 fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
1222 fprintf(stderr,"\n");
1223 }
1224 break;
1225 default:
1226 fprintf(stderr, " %c\n", ge->type);
1227 break;
1228 }
1229 if(ge == end)
1230 mark = ' ';
1231 }
1232 }
1233
1234 /*
1235 * Routine that converts all entries in the path from float to int
1236 */
1237
1238 void
pathtoint(GLYPH * g)1239 pathtoint(
1240 GLYPH *g
1241 )
1242 {
1243 GENTRY *ge;
1244 int x[3], y[3];
1245 int i;
1246
1247
1248 if(ISDBG(TOINT))
1249 fprintf(stderr, "TOINT: glyph %s\n", g->name);
1250 assertisfloat(g, "converting path to int\n");
1251
1252 fdelsmall(g, 1.0); /* get rid of sub-pixel contours */
1253 assertpath(g->entries, __FILE__, __LINE__, g->name);
1254
1255 /* 1st pass, collect the directions of the curves: have
1256 * to do that in advance, while everyting is float
1257 */
1258 for(ge = g->entries; ge != 0; ge = ge->next) {
1259 if( !(ge->flags & GEF_FLOAT) ) {
1260 fprintf(stderr, "**! glyphs %s has int entry, found in conversion to int\n",
1261 g->name);
1262 exit(1);
1263 }
1264 if(ge->type == GE_CURVE) {
1265 ge->dir = fgetcvdir(ge);
1266 }
1267 }
1268
1269 /* now do the conversion */
1270 for(ge = g->entries; ge != 0; ge = ge->next) {
1271 switch(ge->type) {
1272 case GE_MOVE:
1273 case GE_LINE:
1274 if(ISDBG(TOINT))
1275 fprintf(stderr," %c float x=%g y=%g\n", ge->type, ge->fx3, ge->fy3);
1276 x[0] = iround(ge->fx3);
1277 y[0] = iround(ge->fy3);
1278 for(i=0; i<3; i++) { /* put some valid values everywhere, for convenience */
1279 ge->ixn[i] = x[0];
1280 ge->iyn[i] = y[0];
1281 }
1282 if(ISDBG(TOINT))
1283 fprintf(stderr," int x=%d y=%d\n", ge->ix3, ge->iy3);
1284 break;
1285 case GE_CURVE:
1286 if(ISDBG(TOINT))
1287 fprintf(stderr," %c float ", ge->type);
1288
1289 for(i=0; i<3; i++) {
1290 if(ISDBG(TOINT))
1291 fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]);
1292 x[i] = iround(ge->fxn[i]);
1293 y[i] = iround(ge->fyn[i]);
1294 }
1295
1296 if(ISDBG(TOINT))
1297 fprintf(stderr,"\n int ");
1298
1299 for(i=0; i<3; i++) {
1300 ge->ixn[i] = x[i];
1301 ge->iyn[i] = y[i];
1302 if(ISDBG(TOINT))
1303 fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
1304 }
1305 ge->flags &= ~GEF_FLOAT; /* for fixcvdir */
1306 fixcvdir(ge, ge->dir);
1307
1308 if(ISDBG(TOINT)) {
1309 fprintf(stderr,"\n fixed ");
1310 for(i=0; i<3; i++)
1311 fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
1312 fprintf(stderr,"\n");
1313 }
1314
1315 break;
1316 }
1317 ge->flags &= ~GEF_FLOAT;
1318 }
1319 g->flags &= ~GF_FLOAT;
1320 }
1321
1322
1323 /* check whether we can fix up the curve to change its size by (dx,dy) */
1324 /* 0 means NO, 1 means YES */
1325
1326 /* for float: if scaling would be under 10% */
1327
1328 int
fcheckcv(GENTRY * ge,double dx,double dy)1329 fcheckcv(
1330 GENTRY * ge,
1331 double dx,
1332 double dy
1333 )
1334 {
1335 if( !(ge->flags & GEF_FLOAT) ) {
1336 fprintf(stderr, "**! fcheckcv(0x%x) on int entry, ABORT\n", ge);
1337 abort(); /* dump core */
1338 }
1339
1340 if (ge->type != GE_CURVE)
1341 return 0;
1342
1343 if( fabs(ge->fx3 - ge->prev->fx3) < fabs(dx) * 10 )
1344 return 0;
1345
1346 if( fabs(ge->fy3 - ge->prev->fy3) < fabs(dy) * 10 )
1347 return 0;
1348
1349 return 1;
1350 }
1351
1352 /* for int: if won't create new zigzags at the ends */
1353
1354 int
icheckcv(GENTRY * ge,int dx,int dy)1355 icheckcv(
1356 GENTRY * ge,
1357 int dx,
1358 int dy
1359 )
1360 {
1361 int xdep, ydep;
1362
1363 if(ge->flags & GEF_FLOAT) {
1364 fprintf(stderr, "**! icheckcv(0x%x) on floating entry, ABORT\n", ge);
1365 abort(); /* dump core */
1366 }
1367
1368 if (ge->type != GE_CURVE)
1369 return 0;
1370
1371 xdep = ge->ix3 - ge->prev->ix3;
1372 ydep = ge->iy3 - ge->prev->iy3;
1373
1374 if (ge->type == GE_CURVE
1375 && (xdep * (xdep + dx)) > 0
1376 && (ydep * (ydep + dy)) > 0) {
1377 return 1;
1378 } else
1379 return 0;
1380 }
1381
1382 /* float connect the ends of open contours */
1383
1384 void
fclosepaths(GLYPH * g)1385 fclosepaths(
1386 GLYPH * g
1387 )
1388 {
1389 GENTRY *ge, *fge, *xge, *nge;
1390 int i;
1391
1392 assertisfloat(g, "fclosepaths float\n");
1393
1394 for (xge = g->entries; xge != 0; xge = xge->next) {
1395 if( xge->type != GE_PATH )
1396 continue;
1397
1398 ge = xge->prev;
1399 if(ge == 0 || ge->type != GE_LINE && ge->type!= GE_CURVE) {
1400 fprintf(stderr, "**! Glyph %s got empty path\n",
1401 g->name);
1402 exit(1);
1403 }
1404
1405 fge = ge->frwd;
1406 if (fge->prev == 0 || fge->prev->type != GE_MOVE) {
1407 fprintf(stderr, "**! Glyph %s got strange beginning of path\n",
1408 g->name);
1409 exit(1);
1410 }
1411 fge = fge->prev;
1412 if (fge->fx3 != ge->fx3 || fge->fy3 != ge->fy3) {
1413 /* we have to fix this open path */
1414
1415 WARNING_4 fprintf(stderr, "Glyph %s got path open by dx=%g dy=%g\n",
1416 g->name, fge->fx3 - ge->fx3, fge->fy3 - ge->fy3);
1417
1418
1419 /* add a new line */
1420 nge = newgentry(GEF_FLOAT);
1421 (*nge) = (*ge);
1422 nge->fx3 = fge->fx3;
1423 nge->fy3 = fge->fy3;
1424 nge->type = GE_LINE;
1425
1426 addgeafter(ge, nge);
1427
1428 if (fabs(ge->fx3 - fge->fx3) <= 2 && fabs(ge->fy3 - fge->fy3) <= 2) {
1429 /*
1430 * small change, try to get rid of the new entry
1431 */
1432
1433 double df[2];
1434
1435 for(i=0; i<2; i++) {
1436 df[i] = ge->fpoints[i][2] - fge->fpoints[i][2];
1437 df[i] = fclosegap(nge, nge, i, df[i], NULL);
1438 }
1439
1440 if(df[0] == 0. && df[1] == 0.) {
1441 /* closed gap successfully, remove the added entry */
1442 freethisge(nge);
1443 }
1444 }
1445 }
1446 }
1447 }
1448
1449 void
smoothjoints(GLYPH * g)1450 smoothjoints(
1451 GLYPH * g
1452 )
1453 {
1454 GENTRY *ge, *ne;
1455 int dx1, dy1, dx2, dy2, k;
1456 int dir;
1457
1458 return; /* this stuff seems to create problems */
1459
1460 assertisint(g, "smoothjoints int");
1461
1462 if (g->entries == 0) /* nothing to do */
1463 return;
1464
1465 for (ge = g->entries->next; ge != 0; ge = ge->next) {
1466 ne = ge->frwd;
1467
1468 /*
1469 * although there should be no one-line path * and any path
1470 * must end with CLOSEPATH, * nobody can say for sure
1471 */
1472
1473 if (ge == ne || ne == 0)
1474 continue;
1475
1476 /* now handle various joints */
1477
1478 if (ge->type == GE_LINE && ne->type == GE_LINE) {
1479 dx1 = ge->ix3 - ge->prev->ix3;
1480 dy1 = ge->iy3 - ge->prev->iy3;
1481 dx2 = ne->ix3 - ge->ix3;
1482 dy2 = ne->iy3 - ge->iy3;
1483
1484 /* check whether they have the same direction */
1485 /* and the same slope */
1486 /* then we can join them into one line */
1487
1488 if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 && dx1 * dy2 == dy1 * dx2) {
1489 /* extend the previous line */
1490 ge->ix3 = ne->ix3;
1491 ge->iy3 = ne->iy3;
1492
1493 /* and get rid of the next line */
1494 freethisge(ne);
1495 }
1496 } else if (ge->type == GE_LINE && ne->type == GE_CURVE) {
1497 fixcvends(ne);
1498
1499 dx1 = ge->ix3 - ge->prev->ix3;
1500 dy1 = ge->iy3 - ge->prev->iy3;
1501 dx2 = ne->ix1 - ge->ix3;
1502 dy2 = ne->iy1 - ge->iy3;
1503
1504 /* if the line is nearly horizontal and we can fix it */
1505 if (dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0
1506 && icheckcv(ne, 0, -dy1)
1507 && abs(dy1) <= 4) {
1508 dir = igetcvdir(ne);
1509 ge->iy3 -= dy1;
1510 ne->iy1 -= dy1;
1511 fixcvdir(ne, dir);
1512 if (ge->next != ne)
1513 ne->prev->iy3 -= dy1;
1514 dy1 = 0;
1515 } else if (dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0
1516 && icheckcv(ne, -dx1, 0)
1517 && abs(dx1) <= 4) {
1518 /* the same but vertical */
1519 dir = igetcvdir(ne);
1520 ge->ix3 -= dx1;
1521 ne->ix1 -= dx1;
1522 fixcvdir(ne, dir);
1523 if (ge->next != ne)
1524 ne->prev->ix3 -= dx1;
1525 dx1 = 0;
1526 }
1527 /*
1528 * if line is horizontal and curve begins nearly
1529 * horizontally
1530 */
1531 if (dy1 == 0 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0) {
1532 dir = igetcvdir(ne);
1533 ne->iy1 -= dy2;
1534 fixcvdir(ne, dir);
1535 dy2 = 0;
1536 } else if (dx1 == 0 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0) {
1537 /* the same but vertical */
1538 dir = igetcvdir(ne);
1539 ne->ix1 -= dx2;
1540 fixcvdir(ne, dir);
1541 dx2 = 0;
1542 }
1543 } else if (ge->type == GE_CURVE && ne->type == GE_LINE) {
1544 fixcvends(ge);
1545
1546 dx1 = ge->ix3 - ge->ix2;
1547 dy1 = ge->iy3 - ge->iy2;
1548 dx2 = ne->ix3 - ge->ix3;
1549 dy2 = ne->iy3 - ge->iy3;
1550
1551 /* if the line is nearly horizontal and we can fix it */
1552 if (dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0
1553 && icheckcv(ge, 0, dy2)
1554 && abs(dy2) <= 4) {
1555 dir = igetcvdir(ge);
1556 ge->iy3 += dy2;
1557 ge->iy2 += dy2;
1558 fixcvdir(ge, dir);
1559 if (ge->next != ne)
1560 ne->prev->iy3 += dy2;
1561 dy2 = 0;
1562 } else if (dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0
1563 && icheckcv(ge, dx2, 0)
1564 && abs(dx2) <= 4) {
1565 /* the same but vertical */
1566 dir = igetcvdir(ge);
1567 ge->ix3 += dx2;
1568 ge->ix2 += dx2;
1569 fixcvdir(ge, dir);
1570 if (ge->next != ne)
1571 ne->prev->ix3 += dx2;
1572 dx2 = 0;
1573 }
1574 /*
1575 * if line is horizontal and curve ends nearly
1576 * horizontally
1577 */
1578 if (dy2 == 0 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0) {
1579 dir = igetcvdir(ge);
1580 ge->iy2 += dy1;
1581 fixcvdir(ge, dir);
1582 dy1 = 0;
1583 } else if (dx2 == 0 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0) {
1584 /* the same but vertical */
1585 dir = igetcvdir(ge);
1586 ge->ix2 += dx1;
1587 fixcvdir(ge, dir);
1588 dx1 = 0;
1589 }
1590 } else if (ge->type == GE_CURVE && ne->type == GE_CURVE) {
1591 fixcvends(ge);
1592 fixcvends(ne);
1593
1594 dx1 = ge->ix3 - ge->ix2;
1595 dy1 = ge->iy3 - ge->iy2;
1596 dx2 = ne->ix1 - ge->ix3;
1597 dy2 = ne->iy1 - ge->iy3;
1598
1599 /*
1600 * check if we have a rather smooth joint at extremal
1601 * point
1602 */
1603 /* left or right extremal point */
1604 if (abs(dx1) <= 4 && abs(dx2) <= 4
1605 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0
1606 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0
1607 && (ge->iy3 < ge->prev->iy3 && ne->iy3 < ge->iy3
1608 || ge->iy3 > ge->prev->iy3 && ne->iy3 > ge->iy3)
1609 && (ge->ix3 - ge->prev->ix3) * (ne->ix3 - ge->ix3) < 0
1610 ) {
1611 dir = igetcvdir(ge);
1612 ge->ix2 += dx1;
1613 dx1 = 0;
1614 fixcvdir(ge, dir);
1615 dir = igetcvdir(ne);
1616 ne->ix1 -= dx2;
1617 dx2 = 0;
1618 fixcvdir(ne, dir);
1619 }
1620 /* top or down extremal point */
1621 else if (abs(dy1) <= 4 && abs(dy2) <= 4
1622 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0
1623 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0
1624 && (ge->ix3 < ge->prev->ix3 && ne->ix3 < ge->ix3
1625 || ge->ix3 > ge->prev->ix3 && ne->ix3 > ge->ix3)
1626 && (ge->iy3 - ge->prev->iy3) * (ne->iy3 - ge->iy3) < 0
1627 ) {
1628 dir = igetcvdir(ge);
1629 ge->iy2 += dy1;
1630 dy1 = 0;
1631 fixcvdir(ge, dir);
1632 dir = igetcvdir(ne);
1633 ne->iy1 -= dy2;
1634 dy2 = 0;
1635 fixcvdir(ne, dir);
1636 }
1637 /* or may be we just have a smooth junction */
1638 else if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0
1639 && 10 * abs(k = abs(dx1 * dy2) - abs(dy1 * dx2)) < (abs(dx1 * dy2) + abs(dy1 * dx2))) {
1640 int tries[6][4];
1641 int results[6];
1642 int i, b;
1643
1644 /* build array of changes we are going to try */
1645 /* uninitalized entries are 0 */
1646 if (k > 0) {
1647 static int t1[6][4] = {
1648 {0, 0, 0, 0},
1649 {-1, 0, 1, 0},
1650 {-1, 0, 0, 1},
1651 {0, -1, 1, 0},
1652 {0, -1, 0, 1},
1653 {-1, -1, 1, 1}};
1654 memcpy(tries, t1, sizeof tries);
1655 } else {
1656 static int t1[6][4] = {
1657 {0, 0, 0, 0},
1658 {1, 0, -1, 0},
1659 {1, 0, 0, -1},
1660 {0, 1, -1, 0},
1661 {0, 1, 0, -1},
1662 {1, 1, -1, -1}};
1663 memcpy(tries, t1, sizeof tries);
1664 }
1665
1666 /* now try the changes */
1667 results[0] = abs(k);
1668 for (i = 1; i < 6; i++) {
1669 results[i] = abs((abs(dx1) + tries[i][0]) * (abs(dy2) + tries[i][1]) -
1670 (abs(dy1) + tries[i][2]) * (abs(dx2) + tries[i][3]));
1671 }
1672
1673 /* and find the best try */
1674 k = abs(k);
1675 b = 0;
1676 for (i = 1; i < 6; i++)
1677 if (results[i] < k) {
1678 k = results[i];
1679 b = i;
1680 }
1681 /* and finally apply it */
1682 if (dx1 < 0)
1683 tries[b][0] = -tries[b][0];
1684 if (dy2 < 0)
1685 tries[b][1] = -tries[b][1];
1686 if (dy1 < 0)
1687 tries[b][2] = -tries[b][2];
1688 if (dx2 < 0)
1689 tries[b][3] = -tries[b][3];
1690
1691 dir = igetcvdir(ge);
1692 ge->ix2 -= tries[b][0];
1693 ge->iy2 -= tries[b][2];
1694 fixcvdir(ge, dir);
1695 dir = igetcvdir(ne);
1696 ne->ix1 += tries[b][3];
1697 ne->iy1 += tries[b][1];
1698 fixcvdir(ne, dir);
1699 }
1700 }
1701 }
1702 }
1703
1704 /* debugging: print out stems of a glyph */
1705 static void
debugstems(char * name,STEM * hstems,int nhs,STEM * vstems,int nvs)1706 debugstems(
1707 char *name,
1708 STEM * hstems,
1709 int nhs,
1710 STEM * vstems,
1711 int nvs
1712 )
1713 {
1714 int i;
1715
1716 fprintf(pfa_file, "%% %s\n", name);
1717 fprintf(pfa_file, "%% %d horizontal stems:\n", nhs);
1718 for (i = 0; i < nhs; i++)
1719 fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c%c%c\n", i, hstems[i].value,
1720 hstems[i].from, hstems[i].to,
1721 ((hstems[i].flags & ST_UP) ? 'U' : 'D'),
1722 ((hstems[i].flags & ST_END) ? 'E' : '-'),
1723 ((hstems[i].flags & ST_FLAT) ? 'F' : '-'),
1724 ((hstems[i].flags & ST_ZONE) ? 'Z' : ' '),
1725 ((hstems[i].flags & ST_TOPZONE) ? 'T' : ' '));
1726 fprintf(pfa_file, "%% %d vertical stems:\n", nvs);
1727 for (i = 0; i < nvs; i++)
1728 fprintf(pfa_file, "%% %3d %d (%d...%d) %c %c%c\n", i, vstems[i].value,
1729 vstems[i].from, vstems[i].to,
1730 ((vstems[i].flags & ST_UP) ? 'U' : 'D'),
1731 ((vstems[i].flags & ST_END) ? 'E' : '-'),
1732 ((vstems[i].flags & ST_FLAT) ? 'F' : '-'));
1733 }
1734
1735 /* add pseudo-stems for the limits of the Blue zones to the stem array */
1736 static int
addbluestems(STEM * s,int n)1737 addbluestems(
1738 STEM *s,
1739 int n
1740 )
1741 {
1742 int i;
1743
1744 for(i=0; i<nblues && i<2; i+=2) { /* baseline */
1745 s[n].value=bluevalues[i];
1746 s[n].flags=ST_UP|ST_ZONE;
1747 /* don't overlap with anything */
1748 s[n].origin=s[n].from=s[n].to= -10000+i;
1749 n++;
1750 s[n].value=bluevalues[i+1];
1751 s[n].flags=ST_ZONE;
1752 /* don't overlap with anything */
1753 s[n].origin=s[n].from=s[n].to= -10000+i+1;
1754 n++;
1755 }
1756 for(i=2; i<nblues; i+=2) { /* top zones */
1757 s[n].value=bluevalues[i];
1758 s[n].flags=ST_UP|ST_ZONE|ST_TOPZONE;
1759 /* don't overlap with anything */
1760 s[n].origin=s[n].from=s[n].to= -10000+i;
1761 n++;
1762 s[n].value=bluevalues[i+1];
1763 s[n].flags=ST_ZONE|ST_TOPZONE;
1764 /* don't overlap with anything */
1765 s[n].origin=s[n].from=s[n].to= -10000+i+1;
1766 n++;
1767 }
1768 for(i=0; i<notherb; i+=2) { /* bottom zones */
1769 s[n].value=otherblues[i];
1770 s[n].flags=ST_UP|ST_ZONE;
1771 /* don't overlap with anything */
1772 s[n].origin=s[n].from=s[n].to= -10000+i+nblues;
1773 n++;
1774 s[n].value=otherblues[i+1];
1775 s[n].flags=ST_ZONE;
1776 /* don't overlap with anything */
1777 s[n].origin=s[n].from=s[n].to= -10000+i+1+nblues;
1778 n++;
1779 }
1780 return n;
1781 }
1782
1783 /* sort stems in array */
1784 static void
sortstems(STEM * s,int n)1785 sortstems(
1786 STEM * s,
1787 int n
1788 )
1789 {
1790 int i, j;
1791 STEM x;
1792
1793
1794 /* a simple sorting */
1795 /* hm, the ordering criteria are not quite simple :-)
1796 * if the values are tied
1797 * ST_UP always goes under not ST_UP
1798 * ST_ZONE goes on the most outer side
1799 * ST_END goes towards inner side after ST_ZONE
1800 * ST_FLAT goes on the inner side
1801 */
1802
1803 for (i = 0; i < n; i++)
1804 for (j = i + 1; j < n; j++) {
1805 if(s[i].value < s[j].value)
1806 continue;
1807 if(s[i].value == s[j].value) {
1808 if( (s[i].flags & ST_UP) < (s[j].flags & ST_UP) )
1809 continue;
1810 if( (s[i].flags & ST_UP) == (s[j].flags & ST_UP) ) {
1811 if( s[i].flags & ST_UP ) {
1812 if(
1813 (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
1814 >
1815 (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
1816 )
1817 continue;
1818 } else {
1819 if(
1820 (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
1821 <
1822 (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
1823 )
1824 continue;
1825 }
1826 }
1827 }
1828 x = s[j];
1829 s[j] = s[i];
1830 s[i] = x;
1831 }
1832 }
1833
1834 /* check whether two stem borders overlap */
1835
1836 static int
stemoverlap(STEM * s1,STEM * s2)1837 stemoverlap(
1838 STEM * s1,
1839 STEM * s2
1840 )
1841 {
1842 int result;
1843
1844 if (s1->from <= s2->from && s1->to >= s2->from
1845 || s2->from <= s1->from && s2->to >= s1->from)
1846 result = 1;
1847 else
1848 result = 0;
1849
1850 if (ISDBG(STEMOVERLAP))
1851 fprintf(pfa_file, "%% overlap %d(%d..%d)x%d(%d..%d)=%d\n",
1852 s1->value, s1->from, s1->to, s2->value, s2->from, s2->to, result);
1853 return result;
1854 }
1855
1856 /*
1857 * check if the stem [border] is in an appropriate blue zone
1858 * (currently not used)
1859 */
1860
1861 static int
steminblue(STEM * s)1862 steminblue(
1863 STEM *s
1864 )
1865 {
1866 int i, val;
1867
1868 val=s->value;
1869 if(s->flags & ST_UP) {
1870 /* painted size up, look at lower zones */
1871 if(nblues>=2 && val>=bluevalues[0] && val<=bluevalues[1] )
1872 return 1;
1873 for(i=0; i<notherb; i++) {
1874 if( val>=otherblues[i] && val<=otherblues[i+1] )
1875 return 1;
1876 }
1877 } else {
1878 /* painted side down, look at upper zones */
1879 for(i=2; i<nblues; i++) {
1880 if( val>=bluevalues[i] && val<=bluevalues[i+1] )
1881 return 1;
1882 }
1883 }
1884
1885 return 0;
1886 }
1887
1888 /* mark the outermost stem [borders] in the blue zones */
1889
1890 static void
markbluestems(STEM * s,int nold)1891 markbluestems(
1892 STEM *s,
1893 int nold
1894 )
1895 {
1896 int i, j, a, b, c;
1897 /*
1898 * traverse the list of Blue Values, mark the lowest upper
1899 * stem in each bottom zone and the topmost lower stem in
1900 * each top zone with ST_BLUE
1901 */
1902
1903 /* top zones */
1904 for(i=2; i<nblues; i+=2) {
1905 a=bluevalues[i]; b=bluevalues[i+1];
1906 if(ISDBG(BLUESTEMS))
1907 fprintf(pfa_file, "%% looking at blue zone %d...%d\n", a, b);
1908 for(j=nold-1; j>=0; j--) {
1909 if( s[j].flags & (ST_ZONE|ST_UP|ST_END) )
1910 continue;
1911 c=s[j].value;
1912 if(c<a) /* too low */
1913 break;
1914 if(c<=b) { /* found the topmost stem border */
1915 /* mark all the stems with the same value */
1916 if(ISDBG(BLUESTEMS))
1917 fprintf(pfa_file, "%% found D BLUE at %d\n", s[j].value);
1918 /* include ST_END values */
1919 while( s[j+1].value==c && (s[j+1].flags & ST_ZONE)==0 )
1920 j++;
1921 s[j].flags |= ST_BLUE;
1922 for(j--; j>=0 && s[j].value==c
1923 && (s[j].flags & (ST_UP|ST_ZONE))==0 ; j--)
1924 s[j].flags |= ST_BLUE;
1925 break;
1926 }
1927 }
1928 }
1929 /* baseline */
1930 if(nblues>=2) {
1931 a=bluevalues[0]; b=bluevalues[1];
1932 for(j=0; j<nold; j++) {
1933 if( (s[j].flags & (ST_ZONE|ST_UP|ST_END))!=ST_UP )
1934 continue;
1935 c=s[j].value;
1936 if(c>b) /* too high */
1937 break;
1938 if(c>=a) { /* found the lowest stem border */
1939 /* mark all the stems with the same value */
1940 if(ISDBG(BLUESTEMS))
1941 fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value);
1942 /* include ST_END values */
1943 while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 )
1944 j--;
1945 s[j].flags |= ST_BLUE;
1946 for(j++; j<nold && s[j].value==c
1947 && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
1948 s[j].flags |= ST_BLUE;
1949 break;
1950 }
1951 }
1952 }
1953 /* bottom zones: the logic is the same as for baseline */
1954 for(i=0; i<notherb; i+=2) {
1955 a=otherblues[i]; b=otherblues[i+1];
1956 for(j=0; j<nold; j++) {
1957 if( (s[j].flags & (ST_UP|ST_ZONE|ST_END))!=ST_UP )
1958 continue;
1959 c=s[j].value;
1960 if(c>b) /* too high */
1961 break;
1962 if(c>=a) { /* found the lowest stem border */
1963 /* mark all the stems with the same value */
1964 if(ISDBG(BLUESTEMS))
1965 fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value);
1966 /* include ST_END values */
1967 while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 )
1968 j--;
1969 s[j].flags |= ST_BLUE;
1970 for(j++; j<nold && s[j].value==c
1971 && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
1972 s[j].flags |= ST_BLUE;
1973 break;
1974 }
1975 }
1976 }
1977 }
1978
1979 /* Eliminate invalid stems, join equivalent lines and remove nested stems
1980 * to build the main (non-substituted) set of stems.
1981 * XXX add consideration of the italic angle
1982 */
1983 static int
joinmainstems(STEM * s,int nold,int useblues)1984 joinmainstems(
1985 STEM * s,
1986 int nold,
1987 int useblues /* do we use the blue values ? */
1988 )
1989 {
1990 #define MAX_STACK 1000
1991 STEM stack[MAX_STACK];
1992 int nstack = 0;
1993 int sbottom = 0;
1994 int nnew;
1995 int i, j, k;
1996 int a, b, c, w1, w2, w3;
1997 int fw, fd;
1998 /*
1999 * priority of the last found stem:
2000 * 0 - nothing found yet
2001 * 1 - has ST_END in it (one or more)
2002 * 2 - has no ST_END and no ST_FLAT, can override only one stem
2003 * with priority 1
2004 * 3 - has no ST_END and at least one ST_FLAT, can override one
2005 * stem with priority 2 or any number of stems with priority 1
2006 * 4 (handled separately) - has ST_BLUE, can override anything
2007 */
2008 int readystem = 0;
2009 int pri;
2010 int nlps = 0; /* number of non-committed
2011 * lowest-priority stems */
2012
2013
2014 for (i = 0, nnew = 0; i < nold; i++) {
2015 if (s[i].flags & (ST_UP|ST_ZONE)) {
2016 if(s[i].flags & ST_BLUE) {
2017 /* we just HAVE to use this value */
2018 if (readystem)
2019 nnew += 2;
2020 readystem=0;
2021
2022 /* remember the list of Blue zone stems with the same value */
2023 for(a=i, i++; i<nold && s[a].value==s[i].value
2024 && (s[i].flags & ST_BLUE); i++)
2025 {}
2026 b=i; /* our range is a <= i < b */
2027 c= -1; /* index of our best guess up to now */
2028 pri=0;
2029 /* try to find a match, don't cross blue zones */
2030 for(; i<nold && (s[i].flags & ST_BLUE)==0; i++) {
2031 if(s[i].flags & ST_UP) {
2032 if(s[i].flags & ST_TOPZONE)
2033 break;
2034 else
2035 continue;
2036 }
2037 for(j=a; j<b; j++) {
2038 if(!stemoverlap(&s[j], &s[i]) )
2039 continue;
2040 /* consider priorities */
2041 if( ( (s[j].flags|s[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
2042 c=i;
2043 goto bluematch;
2044 }
2045 if( ((s[j].flags|s[i].flags) & ST_END)==0 ) {
2046 if(pri < 2) {
2047 c=i; pri=2;
2048 }
2049 } else {
2050 if(pri == 0) {
2051 c=i; pri=1;
2052 }
2053 }
2054 }
2055 }
2056 bluematch:
2057 /* clean up the stack */
2058 nstack=sbottom=0;
2059 readystem=0;
2060 /* add this stem */
2061 s[nnew++]=s[a];
2062 if(c<0) { /* make one-dot-wide stem */
2063 if(nnew>=b) { /* have no free space */
2064 for(j=nold; j>=b; j--) /* make free space */
2065 s[j]=s[j-1];
2066 b++;
2067 nold++;
2068 }
2069 s[nnew]=s[a];
2070 s[nnew].flags &= ~(ST_UP|ST_BLUE);
2071 nnew++;
2072 i=b-1;
2073 } else {
2074 s[nnew++]=s[c];
2075 i=c; /* skip up to this point */
2076 }
2077 if (ISDBG(MAINSTEMS))
2078 fprintf(pfa_file, "%% +stem %d...%d U BLUE\n",
2079 s[nnew-2].value, s[nnew-1].value);
2080 } else {
2081 if (nstack >= MAX_STACK) {
2082 WARNING_1 fprintf(stderr, "Warning: **** converter's stem stack overflow ****\n");
2083 nstack = 0;
2084 }
2085 stack[nstack++] = s[i];
2086 }
2087 } else if(s[i].flags & ST_BLUE) {
2088 /* again, we just HAVE to use this value */
2089 if (readystem)
2090 nnew += 2;
2091 readystem=0;
2092
2093 /* remember the list of Blue zone stems with the same value */
2094 for(a=i, i++; i<nold && s[a].value==s[i].value
2095 && (s[i].flags & ST_BLUE); i++)
2096 {}
2097 b=i; /* our range is a <= i < b */
2098 c= -1; /* index of our best guess up to now */
2099 pri=0;
2100 /* try to find a match */
2101 for (i = nstack - 1; i >= 0; i--) {
2102 if( (stack[i].flags & ST_UP)==0 ) {
2103 if( (stack[i].flags & (ST_ZONE|ST_TOPZONE))==ST_ZONE )
2104 break;
2105 else
2106 continue;
2107 }
2108 for(j=a; j<b; j++) {
2109 if(!stemoverlap(&s[j], &stack[i]) )
2110 continue;
2111 /* consider priorities */
2112 if( ( (s[j].flags|stack[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
2113 c=i;
2114 goto bluedownmatch;
2115 }
2116 if( ((s[j].flags|stack[i].flags) & ST_END)==0 ) {
2117 if(pri < 2) {
2118 c=i; pri=2;
2119 }
2120 } else {
2121 if(pri == 0) {
2122 c=i; pri=1;
2123 }
2124 }
2125 }
2126 }
2127 bluedownmatch:
2128 /* if found no match make a one-dot-wide stem */
2129 if(c<0) {
2130 c=0;
2131 stack[0]=s[b-1];
2132 stack[0].flags |= ST_UP;
2133 stack[0].flags &= ~ST_BLUE;
2134 }
2135 /* remove all the stems conflicting with this one */
2136 readystem=0;
2137 for(j=nnew-2; j>=0; j-=2) {
2138 if (ISDBG(MAINSTEMS))
2139 fprintf(pfa_file, "%% ?stem %d...%d -- %d\n",
2140 s[j].value, s[j+1].value, stack[c].value);
2141 if(s[j+1].value < stack[c].value) /* no conflict */
2142 break;
2143 if(s[j].flags & ST_BLUE) {
2144 /* oops, we don't want to spoil other blue zones */
2145 stack[c].value=s[j+1].value+1;
2146 break;
2147 }
2148 if( (s[j].flags|s[j+1].flags) & ST_END ) {
2149 if (ISDBG(MAINSTEMS))
2150 fprintf(pfa_file, "%% -stem %d...%d p=1\n",
2151 s[j].value, s[j+1].value);
2152 continue; /* pri==1, silently discard it */
2153 }
2154 /* we want to discard no nore than 2 stems of pri>=2 */
2155 if( ++readystem > 2 ) {
2156 /* change our stem to not conflict */
2157 stack[c].value=s[j+1].value+1;
2158 break;
2159 } else {
2160 if (ISDBG(MAINSTEMS))
2161 fprintf(pfa_file, "%% -stem %d...%d p>=2\n",
2162 s[j].value, s[j+1].value);
2163 continue;
2164 }
2165 }
2166 nnew=j+2;
2167 /* add this stem */
2168 if(nnew>=b-1) { /* have no free space */
2169 for(j=nold; j>=b-1; j--) /* make free space */
2170 s[j]=s[j-1];
2171 b++;
2172 nold++;
2173 }
2174 s[nnew++]=stack[c];
2175 s[nnew++]=s[b-1];
2176 /* clean up the stack */
2177 nstack=sbottom=0;
2178 readystem=0;
2179 /* set the next position to search */
2180 i=b-1;
2181 if (ISDBG(MAINSTEMS))
2182 fprintf(pfa_file, "%% +stem %d...%d D BLUE\n",
2183 s[nnew-2].value, s[nnew-1].value);
2184 } else if (nstack > 0) {
2185
2186 /*
2187 * check whether our stem overlaps with anything in
2188 * stack
2189 */
2190 for (j = nstack - 1; j >= sbottom; j--) {
2191 if (s[i].value <= stack[j].value)
2192 break;
2193 if (stack[j].flags & ST_ZONE)
2194 continue;
2195
2196 if ((s[i].flags & ST_END)
2197 || (stack[j].flags & ST_END))
2198 pri = 1;
2199 else if ((s[i].flags & ST_FLAT)
2200 || (stack[j].flags & ST_FLAT))
2201 pri = 3;
2202 else
2203 pri = 2;
2204
2205 if (pri < readystem && s[nnew + 1].value >= stack[j].value
2206 || !stemoverlap(&stack[j], &s[i]))
2207 continue;
2208
2209 if (readystem > 1 && s[nnew + 1].value < stack[j].value) {
2210 nnew += 2;
2211 readystem = 0;
2212 nlps = 0;
2213 }
2214 /*
2215 * width of the previous stem (if it's
2216 * present)
2217 */
2218 w1 = s[nnew + 1].value - s[nnew].value;
2219
2220 /* width of this stem */
2221 w2 = s[i].value - stack[j].value;
2222
2223 if (readystem == 0) {
2224 /* nothing yet, just add a new stem */
2225 s[nnew] = stack[j];
2226 s[nnew + 1] = s[i];
2227 readystem = pri;
2228 if (pri == 1)
2229 nlps = 1;
2230 else if (pri == 2)
2231 sbottom = j;
2232 else {
2233 sbottom = j + 1;
2234 while (sbottom < nstack
2235 && stack[sbottom].value <= stack[j].value)
2236 sbottom++;
2237 }
2238 if (ISDBG(MAINSTEMS))
2239 fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
2240 stack[j].value, s[i].value, pri, nlps);
2241 } else if (pri == 1) {
2242 if (stack[j].value > s[nnew + 1].value) {
2243 /*
2244 * doesn't overlap with the
2245 * previous one
2246 */
2247 nnew += 2;
2248 nlps++;
2249 s[nnew] = stack[j];
2250 s[nnew + 1] = s[i];
2251 if (ISDBG(MAINSTEMS))
2252 fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
2253 stack[j].value, s[i].value, pri, nlps);
2254 } else if (w2 < w1) {
2255 /* is narrower */
2256 s[nnew] = stack[j];
2257 s[nnew + 1] = s[i];
2258 if (ISDBG(MAINSTEMS))
2259 fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d %d->%d\n",
2260 stack[j].value, s[i].value, pri, nlps, w1, w2);
2261 }
2262 } else if (pri == 2) {
2263 if (readystem == 2) {
2264 /* choose the narrower stem */
2265 if (w1 > w2) {
2266 s[nnew] = stack[j];
2267 s[nnew + 1] = s[i];
2268 sbottom = j;
2269 if (ISDBG(MAINSTEMS))
2270 fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
2271 stack[j].value, s[i].value, pri, nlps);
2272 }
2273 /* else readystem==1 */
2274 } else if (stack[j].value > s[nnew + 1].value) {
2275 /*
2276 * value doesn't overlap with
2277 * the previous one
2278 */
2279 nnew += 2;
2280 nlps = 0;
2281 s[nnew] = stack[j];
2282 s[nnew + 1] = s[i];
2283 sbottom = j;
2284 readystem = pri;
2285 if (ISDBG(MAINSTEMS))
2286 fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
2287 stack[j].value, s[i].value, pri, nlps);
2288 } else if (nlps == 1
2289 || stack[j].value > s[nnew - 1].value) {
2290 /*
2291 * we can replace the top
2292 * stem
2293 */
2294 nlps = 0;
2295 s[nnew] = stack[j];
2296 s[nnew + 1] = s[i];
2297 readystem = pri;
2298 sbottom = j;
2299 if (ISDBG(MAINSTEMS))
2300 fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
2301 stack[j].value, s[i].value, pri, nlps);
2302 }
2303 } else if (readystem == 3) { /* that means also
2304 * pri==3 */
2305 /* choose the narrower stem */
2306 if (w1 > w2) {
2307 s[nnew] = stack[j];
2308 s[nnew + 1] = s[i];
2309 sbottom = j + 1;
2310 while (sbottom < nstack
2311 && stack[sbottom].value <= stack[j].value)
2312 sbottom++;
2313 if (ISDBG(MAINSTEMS))
2314 fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
2315 stack[j].value, s[i].value, pri, nlps);
2316 }
2317 } else if (pri == 3) {
2318 /*
2319 * we can replace as many stems as
2320 * neccessary
2321 */
2322 nnew += 2;
2323 while (nnew > 0 && s[nnew - 1].value >= stack[j].value) {
2324 nnew -= 2;
2325 if (ISDBG(MAINSTEMS))
2326 fprintf(pfa_file, "%% -stem %d..%d\n",
2327 s[nnew].value, s[nnew + 1].value);
2328 }
2329 nlps = 0;
2330 s[nnew] = stack[j];
2331 s[nnew + 1] = s[i];
2332 readystem = pri;
2333 sbottom = j + 1;
2334 while (sbottom < nstack
2335 && stack[sbottom].value <= stack[j].value)
2336 sbottom++;
2337 if (ISDBG(MAINSTEMS))
2338 fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
2339 stack[j].value, s[i].value, pri, nlps);
2340 }
2341 }
2342 }
2343 }
2344 if (readystem)
2345 nnew += 2;
2346
2347 /* change the 1-pixel-wide stems to 20-pixel-wide stems if possible
2348 * the constant 20 is recommended in the Type1 manual
2349 */
2350 if(useblues) {
2351 for(i=0; i<nnew; i+=2) {
2352 if(s[i].value != s[i+1].value)
2353 continue;
2354 if( ((s[i].flags ^ s[i+1].flags) & ST_BLUE)==0 )
2355 continue;
2356 if( s[i].flags & ST_BLUE ) {
2357 if(nnew>i+2 && s[i+2].value<s[i].value+22)
2358 s[i+1].value=s[i+2].value-2; /* compensate for fuzziness */
2359 else
2360 s[i+1].value+=20;
2361 } else {
2362 if(i>0 && s[i-1].value>s[i].value-22)
2363 s[i].value=s[i-1].value+2; /* compensate for fuzziness */
2364 else
2365 s[i].value-=20;
2366 }
2367 }
2368 }
2369 /* make sure that no stem it stretched between
2370 * a top zone and a bottom zone
2371 */
2372 if(useblues) {
2373 for(i=0; i<nnew; i+=2) {
2374 a=10000; /* lowest border of top zone crosing the stem */
2375 b= -10000; /* highest border of bottom zone crossing the stem */
2376
2377 for(j=2; j<nblues; j++) {
2378 c=bluevalues[j];
2379 if( c>=s[i].value && c<=s[i+1].value && c<a )
2380 a=c;
2381 }
2382 if(nblues>=2) {
2383 c=bluevalues[1];
2384 if( c>=s[i].value && c<=s[i+1].value && c>b )
2385 b=c;
2386 }
2387 for(j=1; j<notherb; j++) {
2388 c=otherblues[j];
2389 if( c>=s[i].value && c<=s[i+1].value && c>b )
2390 b=c;
2391 }
2392 if( a!=10000 && b!= -10000 ) { /* it is stretched */
2393 /* split the stem into 2 ghost stems */
2394 for(j=nnew+1; j>i+1; j--) /* make free space */
2395 s[j]=s[j-2];
2396 nnew+=2;
2397
2398 if(s[i].value+22 >= a)
2399 s[i+1].value=a-2; /* leave space for fuzziness */
2400 else
2401 s[i+1].value=s[i].value+20;
2402
2403 if(s[i+3].value-22 <= b)
2404 s[i+2].value=b+2; /* leave space for fuzziness */
2405 else
2406 s[i+2].value=s[i+3].value-20;
2407
2408 i+=2;
2409 }
2410 }
2411 }
2412 /* look for triple stems */
2413 for (i = 0; i < nnew; i += 2) {
2414 if (nnew - i >= 6) {
2415 a = s[i].value + s[i + 1].value;
2416 b = s[i + 2].value + s[i + 3].value;
2417 c = s[i + 4].value + s[i + 5].value;
2418
2419 w1 = s[i + 1].value - s[i].value;
2420 w2 = s[i + 3].value - s[i + 2].value;
2421 w3 = s[i + 5].value - s[i + 4].value;
2422
2423 fw = w3 - w1; /* fuzz in width */
2424 fd = ((c - b) - (b - a)); /* fuzz in distance
2425 * (doubled) */
2426
2427 /* we are able to handle some fuzz */
2428 /*
2429 * it doesn't hurt if the declared stem is a bit
2430 * narrower than actual unless it's an edge in
2431 * a blue zone
2432 */
2433 if (abs(abs(fd) - abs(fw)) * 5 < w2
2434 && abs(fw) * 20 < (w1 + w3)) { /* width dirrerence <10% */
2435
2436 if(useblues) { /* check that we don't disturb any blue stems */
2437 j=c; k=a;
2438 if (fw > 0) {
2439 if (fd > 0) {
2440 if( s[i+5].flags & ST_BLUE )
2441 continue;
2442 j -= fw;
2443 } else {
2444 if( s[i+4].flags & ST_BLUE )
2445 continue;
2446 j += fw;
2447 }
2448 } else if(fw < 0) {
2449 if (fd > 0) {
2450 if( s[i+1].flags & ST_BLUE )
2451 continue;
2452 k -= fw;
2453 } else {
2454 if( s[i].flags & ST_BLUE )
2455 continue;
2456 k += fw;
2457 }
2458 }
2459 pri = ((j - b) - (b - k));
2460
2461 if (pri > 0) {
2462 if( s[i+2].flags & ST_BLUE )
2463 continue;
2464 } else if(pri < 0) {
2465 if( s[i+3].flags & ST_BLUE )
2466 continue;
2467 }
2468 }
2469
2470 /*
2471 * first fix up the width of 1st and 3rd
2472 * stems
2473 */
2474 if (fw > 0) {
2475 if (fd > 0) {
2476 s[i + 5].value -= fw;
2477 c -= fw;
2478 } else {
2479 s[i + 4].value += fw;
2480 c += fw;
2481 }
2482 } else {
2483 if (fd > 0) {
2484 s[i + 1].value -= fw;
2485 a -= fw;
2486 } else {
2487 s[i].value += fw;
2488 a += fw;
2489 }
2490 }
2491 fd = ((c - b) - (b - a));
2492
2493 if (fd > 0) {
2494 s[i + 2].value += abs(fd) / 2;
2495 } else {
2496 s[i + 3].value -= abs(fd) / 2;
2497 }
2498
2499 s[i].flags |= ST_3;
2500 i += 4;
2501 }
2502 }
2503 }
2504
2505 return (nnew & ~1); /* number of lines must be always even */
2506 }
2507
2508 /*
2509 * these macros and function allow to set the base stem,
2510 * check that it's not empty and subtract another stem
2511 * from the base stem (possibly dividing it into multiple parts)
2512 */
2513
2514 /* pairs for pieces of the base stem */
2515 static short xbstem[MAX_STEMS*2];
2516 /* index of the last point */
2517 static int xblast= -1;
2518
2519 #define setbasestem(from, to) \
2520 (xbstem[0]=from, xbstem[1]=to, xblast=1)
2521 #define isbaseempty() (xblast<=0)
2522
2523 /* returns 1 if was overlapping, 0 otherwise */
2524 static int
subfrombase(int from,int to)2525 subfrombase(
2526 int from,
2527 int to
2528 )
2529 {
2530 int a, b;
2531 int i, j;
2532
2533 if(isbaseempty())
2534 return 0;
2535
2536 /* handle the simple case simply */
2537 if(from > xbstem[xblast] || to < xbstem[0])
2538 return 0;
2539
2540 /* the binary search may be more efficient */
2541 /* but for now the linear search is OK */
2542 for(b=1; from > xbstem[b]; b+=2) {} /* result: from <= xbstem[b] */
2543 for(a=xblast-1; to < xbstem[a]; a-=2) {} /* result: to >= xbstem[a] */
2544
2545 /* now the interesting examples are:
2546 * (it was hard for me to understand, so I looked at the examples)
2547 * 1
2548 * a|-----| |-----|b |-----| |-----|
2549 * f|-----|t
2550 * 2
2551 * a|-----|b |-----| |-----| |-----|
2552 * f|--|t
2553 * 3
2554 * a|-----|b |-----| |-----| |-----|
2555 * f|-----|t
2556 * 4
2557 * |-----|b a|-----| |-----| |-----|
2558 * f|------------|t
2559 * 5
2560 * |-----| |-----|b |-----| a|-----|
2561 * f|-----------------------------|t
2562 * 6
2563 * |-----|b |-----| |-----| a|-----|
2564 * f|--------------------------------------------------|t
2565 * 7
2566 * |-----|b |-----| a|-----| |-----|
2567 * f|--------------------------|t
2568 */
2569
2570 if(a < b-1) /* hits a gap - example 1 */
2571 return 0;
2572
2573 /* now the subtraction itself */
2574
2575 if(a==b-1 && from > xbstem[a] && to < xbstem[b]) {
2576 /* overlaps with only one subrange and splits it - example 2 */
2577 j=xblast; i=(xblast+=2);
2578 while(j>=b)
2579 xbstem[i--]=xbstem[j--];
2580 xbstem[b]=from-1;
2581 xbstem[b+1]=to+1;
2582 return 1;
2583 /* becomes
2584 * 2a
2585 * a|b || |-----| |-----| |-----|
2586 * f|--|t
2587 */
2588 }
2589
2590 if(xbstem[b-1] < from) {
2591 /* cuts the back of this subrange - examples 3, 4, 7 */
2592 xbstem[b] = from-1;
2593 b+=2;
2594 /* becomes
2595 * 3a
2596 * a|----| |-----|b |-----| |-----|
2597 * f|-----|t
2598 * 4a
2599 * |---| a|-----|b |-----| |-----|
2600 * f|------------|t
2601 * 7a
2602 * |---| |-----|b a|-----| |-----|
2603 * f|--------------------------|t
2604 */
2605 }
2606
2607 if(xbstem[a+1] > to) {
2608 /* cuts the front of this subrange - examples 4a, 5, 7a */
2609 xbstem[a] = to+1;
2610 a-=2;
2611 /* becomes
2612 * 4b
2613 * a|---| |---|b |-----| |-----|
2614 * f|------------|t
2615 * 5b
2616 * |-----| |-----|b a|-----| ||
2617 * f|-----------------------------|t
2618 * 7b
2619 * |---| a|-----|b || |-----|
2620 * f|--------------------------|t
2621 */
2622 }
2623
2624 if(a < b-1) /* now after modification it hits a gap - examples 3a, 4b */
2625 return 1; /* because we have removed something */
2626
2627 /* now remove the subranges completely covered by the new stem */
2628 /* examples 5b, 6, 7b */
2629 i=b-1; j=a+2;
2630 /* positioned as:
2631 * 5b i j
2632 * |-----| |-----|b a|-----| ||
2633 * f|-----------------------------|t
2634 * 6 i xblast j
2635 * |-----|b |-----| |-----| a|-----|
2636 * f|--------------------------------------------------|t
2637 * 7b i j
2638 * |---| a|-----|b || |-----|
2639 * f|--------------------------|t
2640 */
2641 while(j <= xblast)
2642 xbstem[i++]=xbstem[j++];
2643 xblast=i-1;
2644 return 1;
2645 }
2646
2647 /* for debugging */
2648 static void
printbasestem(void)2649 printbasestem(void)
2650 {
2651 int i;
2652
2653 printf("( ");
2654 for(i=0; i<xblast; i+=2)
2655 printf("%d-%d ", xbstem[i], xbstem[i+1]);
2656 printf(") %d\n", xblast);
2657 }
2658
2659 /*
2660 * Join the stem borders to build the sets of substituted stems
2661 * XXX add consideration of the italic angle
2662 */
2663 static void
joinsubstems(STEM * s,short * pairs,int nold,int useblues)2664 joinsubstems(
2665 STEM * s,
2666 short *pairs,
2667 int nold,
2668 int useblues /* do we use the blue values ? */
2669 )
2670 {
2671 int i, j, x;
2672 static unsigned char mx[MAX_STEMS][MAX_STEMS];
2673
2674 /* we do the substituted groups of stems first
2675 * and it looks like it's going to be REALLY SLOW
2676 * AND PAINFUL but let's bother about it later
2677 */
2678
2679 /* for the substituted stems we don't bother about [hv]stem3 -
2680 * anyway the X11R6 rasterizer does not bother about hstem3
2681 * at all and is able to handle only one global vstem3
2682 * per glyph
2683 */
2684
2685 /* clean the used part of matrix */
2686 for(i=0; i<nold; i++)
2687 for(j=0; j<nold; j++)
2688 mx[i][j]=0;
2689
2690 /* build the matrix of stem pairs */
2691 for(i=0; i<nold; i++) {
2692 if( s[i].flags & ST_ZONE )
2693 continue;
2694 if(s[i].flags & ST_BLUE)
2695 mx[i][i]=1; /* allow to pair with itself if no better pair */
2696 if(s[i].flags & ST_UP) { /* the down-stems are already matched */
2697 setbasestem(s[i].from, s[i].to);
2698 for(j=i+1; j<nold; j++) {
2699 if(s[i].value==s[j].value
2700 || s[j].flags & ST_ZONE ) {
2701 continue;
2702 }
2703 x=subfrombase(s[j].from, s[j].to);
2704
2705 if(s[j].flags & ST_UP) /* match only up+down pairs */
2706 continue;
2707
2708 mx[i][j]=mx[j][i]=x;
2709
2710 if(isbaseempty()) /* nothing else to do */
2711 break;
2712 }
2713 }
2714 }
2715
2716 if(ISDBG(SUBSTEMS)) {
2717 fprintf(pfa_file, "%% ");
2718 for(j=0; j<nold; j++)
2719 putc( j%10==0 ? '0'+(j/10)%10 : ' ', pfa_file);
2720 fprintf(pfa_file, "\n%% ");
2721 for(j=0; j<nold; j++)
2722 putc('0'+j%10, pfa_file);
2723 putc('\n', pfa_file);
2724 for(i=0; i<nold; i++) {
2725 fprintf(pfa_file, "%% %3d ",i);
2726 for(j=0; j<nold; j++)
2727 putc( mx[i][j] ? 'X' : '.', pfa_file);
2728 putc('\n', pfa_file);
2729 }
2730 }
2731
2732 /* now use the matrix to find the best pair for each stem */
2733 for(i=0; i<nold; i++) {
2734 int pri, lastpri, v, f;
2735
2736 x= -1; /* best pair: none */
2737 lastpri=0;
2738
2739 v=s[i].value;
2740 f=s[i].flags;
2741
2742 if(f & ST_ZONE) {
2743 pairs[i]= -1;
2744 continue;
2745 }
2746
2747 if(f & ST_UP) {
2748 for(j=i+1; j<nold; j++) {
2749 if(mx[i][j]==0)
2750 continue;
2751
2752 if( (f | s[j].flags) & ST_END )
2753 pri=1;
2754 else if( (f | s[j].flags) & ST_FLAT )
2755 pri=3;
2756 else
2757 pri=2;
2758
2759 if(lastpri==0
2760 || pri > lastpri
2761 && ( lastpri==1 || s[j].value-v<20 || (s[x].value-v)*2 >= s[j].value-v ) ) {
2762 lastpri=pri;
2763 x=j;
2764 }
2765 }
2766 } else {
2767 for(j=i-1; j>=0; j--) {
2768 if(mx[i][j]==0)
2769 continue;
2770
2771 if( (f | s[j].flags) & ST_END )
2772 pri=1;
2773 else if( (f | s[j].flags) & ST_FLAT )
2774 pri=3;
2775 else
2776 pri=2;
2777
2778 if(lastpri==0
2779 || pri > lastpri
2780 && ( lastpri==1 || v-s[j].value<20 || (v-s[x].value)*2 >= v-s[j].value ) ) {
2781 lastpri=pri;
2782 x=j;
2783 }
2784 }
2785 }
2786 if(x== -1 && mx[i][i])
2787 pairs[i]=i; /* a special case */
2788 else
2789 pairs[i]=x;
2790 }
2791
2792 if(ISDBG(SUBSTEMS)) {
2793 for(i=0; i<nold; i++) {
2794 j=pairs[i];
2795 if(j>0)
2796 fprintf(pfa_file, "%% %d...%d (%d x %d)\n", s[i].value, s[j].value, i, j);
2797 }
2798 }
2799 }
2800
2801 /*
2802 * Make all the stems originating at the same value get the
2803 * same width. Without this the rasterizer may move the dots
2804 * randomly up or down by one pixel, and that looks bad.
2805 * The prioritisation is the same as in findstemat().
2806 */
2807 static void
uniformstems(STEM * s,short * pairs,int ns)2808 uniformstems(
2809 STEM * s,
2810 short *pairs,
2811 int ns
2812 )
2813 {
2814 int i, j, from, to, val, dir;
2815 int pri, prevpri[2], wd, prevwd[2], prevbest[2];
2816
2817 for(from=0; from<ns; from=to) {
2818 prevpri[0] = prevpri[1] = 0;
2819 prevwd[0] = prevwd[1] = 0;
2820 prevbest[0] = prevbest[1] = -1;
2821 val = s[from].value;
2822
2823 for(to = from; to<ns && s[to].value == val; to++) {
2824 dir = ((s[to].flags & ST_UP)!=0);
2825
2826 i=pairs[to]; /* the other side of this stem */
2827 if(i<0 || i==to)
2828 continue; /* oops, no other side */
2829 wd=abs(s[i].value-val);
2830 if(wd == 0)
2831 continue;
2832 pri=1;
2833 if( (s[to].flags | s[i].flags) & ST_END )
2834 pri=0;
2835 if( prevbest[dir] == -1 || pri > prevpri[dir] || wd<prevwd[dir] ) {
2836 prevbest[dir]=i;
2837 prevpri[dir]=pri;
2838 prevwd[dir]=wd;
2839 }
2840 }
2841
2842 for(i=from; i<to; i++) {
2843 dir = ((s[i].flags & ST_UP)!=0);
2844 if(prevbest[dir] >= 0) {
2845 if(ISDBG(SUBSTEMS)) {
2846 fprintf(stderr, "at %d (%s %d) pair %d->%d(%d)\n", i,
2847 (dir ? "UP":"DOWN"), s[i].value, pairs[i], prevbest[dir],
2848 s[prevbest[dir]].value);
2849 }
2850 pairs[i] = prevbest[dir];
2851 }
2852 }
2853 }
2854 }
2855
2856 /*
2857 * Find the best stem in the array at the specified (value, origin),
2858 * related to the entry ge.
2859 * Returns its index in the array sp, -1 means "none".
2860 * prevbest is the result for the other end of the line, we must
2861 * find something better than it or leave it as it is.
2862 */
2863 static int
findstemat(int value,int origin,GENTRY * ge,STEM * sp,short * pairs,int ns,int prevbest)2864 findstemat(
2865 int value,
2866 int origin,
2867 GENTRY *ge,
2868 STEM *sp,
2869 short *pairs,
2870 int ns,
2871 int prevbest /* -1 means "none" */
2872 )
2873 {
2874 int i, min, max;
2875 int v, si;
2876 int pri, prevpri; /* priority, 0 = has ST_END, 1 = no ST_END */
2877 int wd, prevwd; /* stem width */
2878
2879 si= -1; /* nothing yet */
2880
2881 /* stems are ordered by value, binary search */
2882 min=0; max=ns; /* min <= i < max */
2883 while( min < max ) {
2884 i=(min+max)/2;
2885 v=sp[i].value;
2886 if(v<value)
2887 min=i+1;
2888 else if(v>value)
2889 max=i;
2890 else {
2891 si=i; /* temporary value */
2892 break;
2893 }
2894 }
2895
2896 if( si < 0 ) /* found nothing this time */
2897 return prevbest;
2898
2899 /* find the priority of the prevbest */
2900 /* we expect that prevbest has a pair */
2901 if(prevbest>=0) {
2902 i=pairs[prevbest];
2903 prevpri=1;
2904 if( (sp[prevbest].flags | sp[i].flags) & ST_END )
2905 prevpri=0;
2906 prevwd=abs(sp[i].value-value);
2907 }
2908
2909 /* stems are not ordered by origin, so now do the linear search */
2910
2911 while( si>0 && sp[si-1].value==value ) /* find the first one */
2912 si--;
2913
2914 for(; si<ns && sp[si].value==value; si++) {
2915 if(sp[si].origin != origin)
2916 continue;
2917 if(sp[si].ge != ge) {
2918 if(ISDBG(SUBSTEMS)) {
2919 fprintf(stderr,
2920 "dbg: possible self-intersection at v=%d o=%d exp_ge=0x%x ge=0x%x\n",
2921 value, origin, ge, sp[si].ge);
2922 }
2923 continue;
2924 }
2925 i=pairs[si]; /* the other side of this stem */
2926 if(i<0)
2927 continue; /* oops, no other side */
2928 pri=1;
2929 if( (sp[si].flags | sp[i].flags) & ST_END )
2930 pri=0;
2931 wd=abs(sp[i].value-value);
2932 if( prevbest == -1 || pri >prevpri
2933 || pri==prevpri && prevwd==0 || wd!=0 && wd<prevwd ) {
2934 prevbest=si;
2935 prevpri=pri;
2936 prevwd=wd;
2937 continue;
2938 }
2939 }
2940
2941 return prevbest;
2942 }
2943
2944 /* add the substems for one glyph entry
2945 * (called from groupsubstems())
2946 * returns 0 if all OK, 1 if too many groups
2947 */
2948
2949 static int gssentry_lastgrp=0; /* reset to 0 for each new glyph */
2950
2951 static int
gssentry(GENTRY * ge,STEM * hs,short * hpairs,int nhs,STEM * vs,short * vpairs,int nvs,STEMBOUNDS * s,short * egp,int * nextvsi,int * nexthsi)2952 gssentry( /* crazy number of parameters */
2953 GENTRY *ge,
2954 STEM *hs, /* horizontal stems, sorted by value */
2955 short *hpairs,
2956 int nhs,
2957 STEM *vs, /* vertical stems, sorted by value */
2958 short *vpairs,
2959 int nvs,
2960 STEMBOUNDS *s,
2961 short *egp,
2962 int *nextvsi,
2963 int *nexthsi /* -2 means "check by yourself" */
2964 ) {
2965 enum {
2966 SI_VP, /* vertical primary */
2967 SI_HP, /* horizontal primary */
2968 SI_SIZE /* size of the array */
2969 };
2970 int si[SI_SIZE]; /* indexes of relevant stems */
2971
2972 /* the bounds of the existing relevant stems */
2973 STEMBOUNDS r[ sizeof(si) / sizeof(si[0]) * 2 ];
2974 char rexpand; /* by how much we need to expand the group */
2975 int nr; /* and the number of them */
2976
2977 /* yet more temporary storage */
2978 short lb, hb, isvert;
2979 int conflict, grp;
2980 int i, j, x, y;
2981
2982
2983 /* for each line or curve we try to find a horizontal and
2984 * a vertical stem corresponding to its first point
2985 * (corresponding to the last point of the previous
2986 * glyph entry), because the directions of the lines
2987 * will be eventually reversed and it will then become the last
2988 * point. And the T1 rasterizer applies the hints to
2989 * the last point.
2990 *
2991 */
2992
2993 /* start with the common part, the first point */
2994 x=ge->prev->ix3;
2995 y=ge->prev->iy3;
2996
2997 if(*nextvsi == -2)
2998 si[SI_VP]=findstemat(x, y, ge, vs, vpairs, nvs, -1);
2999 else {
3000 si[SI_VP]= *nextvsi; *nextvsi= -2;
3001 }
3002 if(*nexthsi == -2)
3003 si[SI_HP]=findstemat(y, x, ge, hs, hpairs, nhs, -1);
3004 else {
3005 si[SI_HP]= *nexthsi; *nexthsi= -2;
3006 }
3007
3008 /*
3009 * For the horizontal lines we make sure that both
3010 * ends of the line have the same horizontal stem,
3011 * and the same thing for vertical lines and stems.
3012 * In both cases we enforce the stem for the next entry.
3013 * Otherwise unpleasant effects may arise.
3014 */
3015
3016 if(ge->type==GE_LINE) {
3017 if(ge->ix3==x) { /* vertical line */
3018 *nextvsi=si[SI_VP]=findstemat(x, ge->iy3, ge->frwd, vs, vpairs, nvs, si[SI_VP]);
3019 } else if(ge->iy3==y) { /* horizontal line */
3020 *nexthsi=si[SI_HP]=findstemat(y, ge->ix3, ge->frwd, hs, hpairs, nhs, si[SI_HP]);
3021 }
3022 }
3023
3024 if(si[SI_VP]+si[SI_HP] == -2) /* no stems, leave it alone */
3025 return 0;
3026
3027 /* build the array of relevant bounds */
3028 nr=0;
3029 for(i=0; i< sizeof(si) / sizeof(si[0]); i++) {
3030 STEM *sp;
3031 short *pairs;
3032 int step;
3033 int f;
3034 int nzones, firstzone, binzone, einzone;
3035 int btype, etype;
3036
3037 if(si[i] < 0)
3038 continue;
3039
3040 if(i<SI_HP) {
3041 r[nr].isvert=1; sp=vs; pairs=vpairs;
3042 } else {
3043 r[nr].isvert=0; sp=hs; pairs=hpairs;
3044 }
3045
3046 r[nr].low=sp[ si[i] ].value;
3047 r[nr].high=sp[ pairs[ si[i] ] ].value;
3048
3049 if(r[nr].low > r[nr].high) {
3050 j=r[nr].low; r[nr].low=r[nr].high; r[nr].high=j;
3051 step= -1;
3052 } else {
3053 step=1;
3054 }
3055
3056 /* handle the interaction with Blue Zones */
3057
3058 if(i>=SI_HP) { /* only for horizontal stems */
3059 if(si[i]==pairs[si[i]]) {
3060 /* special case, the outermost stem in the
3061 * Blue Zone without a pair, simulate it to 20-pixel
3062 */
3063 if(sp[ si[i] ].flags & ST_UP) {
3064 r[nr].high+=20;
3065 for(j=si[i]+1; j<nhs; j++)
3066 if( (sp[j].flags & (ST_ZONE|ST_TOPZONE))
3067 == (ST_ZONE|ST_TOPZONE) ) {
3068 if(r[nr].high > sp[j].value-2)
3069 r[nr].high=sp[j].value-2;
3070 break;
3071 }
3072 } else {
3073 r[nr].low-=20;
3074 for(j=si[i]-1; j>=0; j--)
3075 if( (sp[j].flags & (ST_ZONE|ST_TOPZONE))
3076 == (ST_ZONE) ) {
3077 if(r[nr].low < sp[j].value+2)
3078 r[nr].low=sp[j].value+2;
3079 break;
3080 }
3081 }
3082 }
3083
3084 /* check that the stem borders don't end up in
3085 * different Blue Zones */
3086 f=sp[ si[i] ].flags;
3087 nzones=0; einzone=binzone=0;
3088 for(j=si[i]; j!=pairs[ si[i] ]; j+=step) {
3089 if( (sp[j].flags & ST_ZONE)==0 )
3090 continue;
3091 /* if see a zone border going in the same direction */
3092 if( ((f ^ sp[j].flags) & ST_UP)==0 ) {
3093 if( ++nzones == 1 ) {
3094 firstzone=sp[j].value; /* remember the first one */
3095 etype=sp[j].flags & ST_TOPZONE;
3096 }
3097 einzone=1;
3098
3099 } else { /* the opposite direction */
3100 if(nzones==0) { /* beginning is in a blue zone */
3101 binzone=1;
3102 btype=sp[j].flags & ST_TOPZONE;
3103 }
3104 einzone=0;
3105 }
3106 }
3107
3108 /* beginning and end are in Blue Zones of different types */
3109 if( binzone && einzone && (btype ^ etype)!=0 ) {
3110 if( sp[si[i]].flags & ST_UP ) {
3111 if(firstzone > r[nr].low+22)
3112 r[nr].high=r[nr].low+20;
3113 else
3114 r[nr].high=firstzone-2;
3115 } else {
3116 if(firstzone < r[nr].high-22)
3117 r[nr].low=r[nr].high-20;
3118 else
3119 r[nr].low=firstzone+2;
3120 }
3121 }
3122 }
3123
3124 if(ISDBG(SUBSTEMS))
3125 fprintf(pfa_file, "%% at(%d,%d)[%d,%d] %d..%d %c (%d x %d)\n", x, y, i, nr,
3126 r[nr].low, r[nr].high, r[nr].isvert ? 'v' : 'h',
3127 si[i], pairs[si[i]]);
3128
3129 nr++;
3130 }
3131
3132 /* now try to find a group */
3133 conflict=0; /* no conflicts found yet */
3134 for(j=0; j<nr; j++)
3135 r[j].already=0;
3136
3137 /* check if it fits into the last group */
3138 grp = gssentry_lastgrp;
3139 i = (grp==0)? 0 : egp[grp-1];
3140 for(; i<egp[grp]; i++) {
3141 lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
3142 for(j=0; j<nr; j++)
3143 if( r[j].isvert==isvert /* intersects */
3144 && r[j].low <= hb && r[j].high >= lb ) {
3145 if( r[j].low == lb && r[j].high == hb ) /* coincides */
3146 r[j].already=1;
3147 else
3148 conflict=1;
3149 }
3150
3151 if(conflict)
3152 break;
3153 }
3154
3155 if(conflict) { /* nope, check all the groups */
3156 for(j=0; j<nr; j++)
3157 r[j].already=0;
3158
3159 for(i=0, grp=0; i<egp[NSTEMGRP-1]; i++) {
3160 if(i == egp[grp]) { /* checked all stems in a group */
3161 if(conflict) {
3162 grp++; conflict=0; /* check the next group */
3163 for(j=0; j<nr; j++)
3164 r[j].already=0;
3165 } else
3166 break; /* insert into this group */
3167 }
3168
3169 lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
3170 for(j=0; j<nr; j++)
3171 if( r[j].isvert==isvert /* intersects */
3172 && r[j].low <= hb && r[j].high >= lb ) {
3173 if( r[j].low == lb && r[j].high == hb ) /* coincides */
3174 r[j].already=1;
3175 else
3176 conflict=1;
3177 }
3178
3179 if(conflict)
3180 i=egp[grp]-1; /* fast forward to the next group */
3181 }
3182 }
3183
3184 /* do we have any empty group ? */
3185 if(conflict && grp < NSTEMGRP-1) {
3186 grp++; conflict=0;
3187 for(j=0; j<nr; j++)
3188 r[j].already=0;
3189 }
3190
3191 if(conflict) { /* oops, can't find any group to fit */
3192 return 1;
3193 }
3194
3195 /* OK, add stems to this group */
3196
3197 rexpand = nr;
3198 for(j=0; j<nr; j++)
3199 rexpand -= r[j].already;
3200
3201 if(rexpand > 0) {
3202 for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--)
3203 s[i+rexpand]=s[i];
3204 for(i=0; i<nr; i++)
3205 if(!r[i].already)
3206 s[egp[grp]++]=r[i];
3207 for(i=grp+1; i<NSTEMGRP; i++)
3208 egp[i]+=rexpand;
3209 }
3210
3211 ge->stemid = gssentry_lastgrp = grp;
3212 return 0;
3213 }
3214
3215 /*
3216 * Create the groups of substituted stems from the list.
3217 * Each group will be represented by a subroutine in the Subs
3218 * array.
3219 */
3220
3221 static void
groupsubstems(GLYPH * g,STEM * hs,short * hpairs,int nhs,STEM * vs,short * vpairs,int nvs)3222 groupsubstems(
3223 GLYPH *g,
3224 STEM *hs, /* horizontal stems, sorted by value */
3225 short *hpairs,
3226 int nhs,
3227 STEM *vs, /* vertical stems, sorted by value */
3228 short *vpairs,
3229 int nvs
3230 )
3231 {
3232 GENTRY *ge;
3233 int i, j;
3234
3235 /* temporary storage */
3236 STEMBOUNDS s[MAX_STEMS*2];
3237 /* indexes in there, pointing past the end each stem group */
3238 short egp[NSTEMGRP];
3239
3240 int nextvsi, nexthsi; /* -2 means "check by yourself" */
3241
3242 for(i=0; i<NSTEMGRP; i++)
3243 egp[i]=0;
3244
3245 nextvsi=nexthsi= -2; /* processed no horiz/vert line */
3246
3247 gssentry_lastgrp = 0; /* reset the last group for new glyph */
3248
3249 for (ge = g->entries; ge != 0; ge = ge->next) {
3250 if(ge->type!=GE_LINE && ge->type!=GE_CURVE) {
3251 nextvsi=nexthsi= -2; /* next path is independent */
3252 continue;
3253 }
3254
3255 if( gssentry(ge, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) {
3256 WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n",
3257 g->name, NSTEMGRP);
3258 /* it's better to have no substituted hints at all than have only part */
3259 for (ge = g->entries; ge != 0; ge = ge->next)
3260 ge->stemid= -1;
3261 g->nsg=0; /* just to be safe, already is 0 by initialization */
3262 return;
3263 }
3264
3265 /*
3266 * handle the last vert/horiz line of the path specially,
3267 * correct the hint for the first entry of the path
3268 */
3269 if(ge->frwd != ge->next && (nextvsi != -2 || nexthsi != -2) ) {
3270 if( gssentry(ge->frwd, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) {
3271 WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n",
3272 g->name, NSTEMGRP);
3273 /* it's better to have no substituted hints at all than have only part */
3274 for (ge = g->entries; ge != 0; ge = ge->next)
3275 ge->stemid= -1;
3276 g->nsg=0; /* just to be safe, already is 0 by initialization */
3277 return;
3278 }
3279 }
3280
3281 }
3282
3283 /* find the index of the first empty group - same as the number of groups */
3284 if(egp[0]>0) {
3285 for(i=1; i<NSTEMGRP && egp[i]!=egp[i-1]; i++)
3286 {}
3287 g->nsg=i;
3288 } else
3289 g->nsg=0;
3290
3291 if(ISDBG(SUBSTEMS)) {
3292 fprintf(pfa_file, "%% %d substem groups (%d %d %d)\n", g->nsg,
3293 g->nsg>1 ? egp[g->nsg-2] : -1,
3294 g->nsg>0 ? egp[g->nsg-1] : -1,
3295 g->nsg<NSTEMGRP ? egp[g->nsg] : -1 );
3296 j=0;
3297 for(i=0; i<g->nsg; i++) {
3298 fprintf(pfa_file, "%% grp %3d: ", i);
3299 for(; j<egp[i]; j++) {
3300 fprintf(pfa_file, " %4d...%-4d %c ", s[j].low, s[j].high,
3301 s[j].isvert ? 'v' : 'h');
3302 }
3303 fprintf(pfa_file, "\n");
3304 }
3305 }
3306
3307 if(g->nsg==1) { /* it would be the same as the main stems */
3308 /* so erase it */
3309 for (ge = g->entries; ge != 0; ge = ge->next)
3310 ge->stemid= -1;
3311 g->nsg=0;
3312 }
3313
3314 if(g->nsg>0) {
3315 if( (g->nsbs=malloc(g->nsg * sizeof (egp[0]))) == 0 ) {
3316 fprintf(stderr, "**** not enough memory for substituted hints ****\n");
3317 exit(255);
3318 }
3319 memmove(g->nsbs, egp, g->nsg * sizeof(short));
3320 if( (g->sbstems=malloc(egp[g->nsg-1] * sizeof (s[0]))) == 0 ) {
3321 fprintf(stderr, "**** not enough memory for substituted hints ****\n");
3322 exit(255);
3323 }
3324 memmove(g->sbstems, s, egp[g->nsg-1] * sizeof(s[0]));
3325 }
3326 }
3327
3328 void
buildstems(GLYPH * g)3329 buildstems(
3330 GLYPH * g
3331 )
3332 {
3333 STEM hs[MAX_STEMS], vs[MAX_STEMS]; /* temporary working
3334 * storage */
3335 short hs_pairs[MAX_STEMS], vs_pairs[MAX_STEMS]; /* best pairs for these stems */
3336 STEM *sp;
3337 GENTRY *ge, *nge, *pge;
3338 int nx, ny;
3339 int ovalue;
3340 int totals, grp, lastgrp;
3341
3342 assertisint(g, "buildstems int");
3343
3344 g->nhs = g->nvs = 0;
3345 memset(hs, 0, sizeof hs);
3346 memset(vs, 0, sizeof vs);
3347
3348 /* first search the whole character for possible stem points */
3349
3350 for (ge = g->entries; ge != 0; ge = ge->next) {
3351 if (ge->type == GE_CURVE) {
3352
3353 /*
3354 * SURPRISE!
3355 * We consider the stems bound by the
3356 * H/V ends of the curves as flat ones.
3357 *
3358 * But we don't include the point on the
3359 * other end into the range.
3360 */
3361
3362 /* first check the beginning of curve */
3363 /* if it is horizontal, add a hstem */
3364 if (ge->iy1 == ge->prev->iy3) {
3365 hs[g->nhs].value = ge->iy1;
3366
3367 if (ge->ix1 < ge->prev->ix3)
3368 hs[g->nhs].flags = ST_FLAT | ST_UP;
3369 else
3370 hs[g->nhs].flags = ST_FLAT;
3371
3372 hs[g->nhs].origin = ge->prev->ix3;
3373 hs[g->nhs].ge = ge;
3374
3375 if (ge->ix1 < ge->prev->ix3) {
3376 hs[g->nhs].from = ge->ix1+1;
3377 hs[g->nhs].to = ge->prev->ix3;
3378 if(hs[g->nhs].from > hs[g->nhs].to)
3379 hs[g->nhs].from--;
3380 } else {
3381 hs[g->nhs].from = ge->prev->ix3;
3382 hs[g->nhs].to = ge->ix1-1;
3383 if(hs[g->nhs].from > hs[g->nhs].to)
3384 hs[g->nhs].to++;
3385 }
3386 if (ge->ix1 != ge->prev->ix3)
3387 g->nhs++;
3388 }
3389 /* if it is vertical, add a vstem */
3390 else if (ge->ix1 == ge->prev->ix3) {
3391 vs[g->nvs].value = ge->ix1;
3392
3393 if (ge->iy1 > ge->prev->iy3)
3394 vs[g->nvs].flags = ST_FLAT | ST_UP;
3395 else
3396 vs[g->nvs].flags = ST_FLAT;
3397
3398 vs[g->nvs].origin = ge->prev->iy3;
3399 vs[g->nvs].ge = ge;
3400
3401 if (ge->iy1 < ge->prev->iy3) {
3402 vs[g->nvs].from = ge->iy1+1;
3403 vs[g->nvs].to = ge->prev->iy3;
3404 if(vs[g->nvs].from > vs[g->nvs].to)
3405 vs[g->nvs].from--;
3406 } else {
3407 vs[g->nvs].from = ge->prev->iy3;
3408 vs[g->nvs].to = ge->iy1-1;
3409 if(vs[g->nvs].from > vs[g->nvs].to)
3410 vs[g->nvs].to++;
3411 }
3412
3413 if (ge->iy1 != ge->prev->iy3)
3414 g->nvs++;
3415 }
3416 /* then check the end of curve */
3417 /* if it is horizontal, add a hstem */
3418 if (ge->iy3 == ge->iy2) {
3419 hs[g->nhs].value = ge->iy3;
3420
3421 if (ge->ix3 < ge->ix2)
3422 hs[g->nhs].flags = ST_FLAT | ST_UP;
3423 else
3424 hs[g->nhs].flags = ST_FLAT;
3425
3426 hs[g->nhs].origin = ge->ix3;
3427 hs[g->nhs].ge = ge->frwd;
3428
3429 if (ge->ix3 < ge->ix2) {
3430 hs[g->nhs].from = ge->ix3;
3431 hs[g->nhs].to = ge->ix2-1;
3432 if( hs[g->nhs].from > hs[g->nhs].to )
3433 hs[g->nhs].to++;
3434 } else {
3435 hs[g->nhs].from = ge->ix2+1;
3436 hs[g->nhs].to = ge->ix3;
3437 if( hs[g->nhs].from > hs[g->nhs].to )
3438 hs[g->nhs].from--;
3439 }
3440
3441 if (ge->ix3 != ge->ix2)
3442 g->nhs++;
3443 }
3444 /* if it is vertical, add a vstem */
3445 else if (ge->ix3 == ge->ix2) {
3446 vs[g->nvs].value = ge->ix3;
3447
3448 if (ge->iy3 > ge->iy2)
3449 vs[g->nvs].flags = ST_FLAT | ST_UP;
3450 else
3451 vs[g->nvs].flags = ST_FLAT;
3452
3453 vs[g->nvs].origin = ge->iy3;
3454 vs[g->nvs].ge = ge->frwd;
3455
3456 if (ge->iy3 < ge->iy2) {
3457 vs[g->nvs].from = ge->iy3;
3458 vs[g->nvs].to = ge->iy2-1;
3459 if( vs[g->nvs].from > vs[g->nvs].to )
3460 vs[g->nvs].to++;
3461 } else {
3462 vs[g->nvs].from = ge->iy2+1;
3463 vs[g->nvs].to = ge->iy3;
3464 if( vs[g->nvs].from > vs[g->nvs].to )
3465 vs[g->nvs].from--;
3466 }
3467
3468 if (ge->iy3 != ge->iy2)
3469 g->nvs++;
3470 } else {
3471
3472 /*
3473 * check the end of curve for a not smooth
3474 * local extremum
3475 */
3476 nge = ge->frwd;
3477
3478 if (nge == 0)
3479 continue;
3480 else if (nge->type == GE_LINE) {
3481 nx = nge->ix3;
3482 ny = nge->iy3;
3483 } else if (nge->type == GE_CURVE) {
3484 nx = nge->ix1;
3485 ny = nge->iy1;
3486 } else
3487 continue;
3488
3489 /* check for vertical extremums */
3490 if (ge->iy3 > ge->iy2 && ge->iy3 > ny
3491 || ge->iy3 < ge->iy2 && ge->iy3 < ny) {
3492 hs[g->nhs].value = ge->iy3;
3493 hs[g->nhs].from
3494 = hs[g->nhs].to
3495 = hs[g->nhs].origin = ge->ix3;
3496 hs[g->nhs].ge = ge->frwd;
3497
3498 if (ge->ix3 < ge->ix2
3499 || nx < ge->ix3)
3500 hs[g->nhs].flags = ST_UP;
3501 else
3502 hs[g->nhs].flags = 0;
3503
3504 if (ge->ix3 != ge->ix2 || nx != ge->ix3)
3505 g->nhs++;
3506 }
3507 /*
3508 * the same point may be both horizontal and
3509 * vertical extremum
3510 */
3511 /* check for horizontal extremums */
3512 if (ge->ix3 > ge->ix2 && ge->ix3 > nx
3513 || ge->ix3 < ge->ix2 && ge->ix3 < nx) {
3514 vs[g->nvs].value = ge->ix3;
3515 vs[g->nvs].from
3516 = vs[g->nvs].to
3517 = vs[g->nvs].origin = ge->iy3;
3518 vs[g->nvs].ge = ge->frwd;
3519
3520 if (ge->iy3 > ge->iy2
3521 || ny > ge->iy3)
3522 vs[g->nvs].flags = ST_UP;
3523 else
3524 vs[g->nvs].flags = 0;
3525
3526 if (ge->iy3 != ge->iy2 || ny != ge->iy3)
3527 g->nvs++;
3528 }
3529 }
3530
3531 } else if (ge->type == GE_LINE) {
3532 nge = ge->frwd;
3533
3534 /* if it is horizontal, add a hstem */
3535 /* and the ends as vstems if they brace the line */
3536 if (ge->iy3 == ge->prev->iy3
3537 && ge->ix3 != ge->prev->ix3) {
3538 hs[g->nhs].value = ge->iy3;
3539 if (ge->ix3 < ge->prev->ix3) {
3540 hs[g->nhs].flags = ST_FLAT | ST_UP;
3541 hs[g->nhs].from = ge->ix3;
3542 hs[g->nhs].to = ge->prev->ix3;
3543 } else {
3544 hs[g->nhs].flags = ST_FLAT;
3545 hs[g->nhs].from = ge->prev->ix3;
3546 hs[g->nhs].to = ge->ix3;
3547 }
3548 hs[g->nhs].origin = ge->ix3;
3549 hs[g->nhs].ge = ge->frwd;
3550
3551 pge = ge->bkwd;
3552
3553 /* add beginning as vstem */
3554 vs[g->nvs].value = pge->ix3;
3555 vs[g->nvs].origin
3556 = vs[g->nvs].from
3557 = vs[g->nvs].to = pge->iy3;
3558 vs[g->nvs].ge = ge;
3559
3560 if(pge->type==GE_CURVE)
3561 ovalue=pge->iy2;
3562 else
3563 ovalue=pge->prev->iy3;
3564
3565 if (pge->iy3 > ovalue)
3566 vs[g->nvs].flags = ST_UP | ST_END;
3567 else if (pge->iy3 < ovalue)
3568 vs[g->nvs].flags = ST_END;
3569 else
3570 vs[g->nvs].flags = 0;
3571
3572 if( vs[g->nvs].flags != 0 )
3573 g->nvs++;
3574
3575 /* add end as vstem */
3576 vs[g->nvs].value = ge->ix3;
3577 vs[g->nvs].origin
3578 = vs[g->nvs].from
3579 = vs[g->nvs].to = ge->iy3;
3580 vs[g->nvs].ge = ge->frwd;
3581
3582 if(nge->type==GE_CURVE)
3583 ovalue=nge->iy1;
3584 else
3585 ovalue=nge->iy3;
3586
3587 if (ovalue > ge->iy3)
3588 vs[g->nvs].flags = ST_UP | ST_END;
3589 else if (ovalue < ge->iy3)
3590 vs[g->nvs].flags = ST_END;
3591 else
3592 vs[g->nvs].flags = 0;
3593
3594 if( vs[g->nvs].flags != 0 )
3595 g->nvs++;
3596
3597 g->nhs++;
3598 }
3599 /* if it is vertical, add a vstem */
3600 /* and the ends as hstems if they brace the line */
3601 else if (ge->ix3 == ge->prev->ix3
3602 && ge->iy3 != ge->prev->iy3) {
3603 vs[g->nvs].value = ge->ix3;
3604 if (ge->iy3 > ge->prev->iy3) {
3605 vs[g->nvs].flags = ST_FLAT | ST_UP;
3606 vs[g->nvs].from = ge->prev->iy3;
3607 vs[g->nvs].to = ge->iy3;
3608 } else {
3609 vs[g->nvs].flags = ST_FLAT;
3610 vs[g->nvs].from = ge->iy3;
3611 vs[g->nvs].to = ge->prev->iy3;
3612 }
3613 vs[g->nvs].origin = ge->iy3;
3614 vs[g->nvs].ge = ge->frwd;
3615
3616 pge = ge->bkwd;
3617
3618 /* add beginning as hstem */
3619 hs[g->nhs].value = pge->iy3;
3620 hs[g->nhs].origin
3621 = hs[g->nhs].from
3622 = hs[g->nhs].to = pge->ix3;
3623 hs[g->nhs].ge = ge;
3624
3625 if(pge->type==GE_CURVE)
3626 ovalue=pge->ix2;
3627 else
3628 ovalue=pge->prev->ix3;
3629
3630 if (pge->ix3 < ovalue)
3631 hs[g->nhs].flags = ST_UP | ST_END;
3632 else if (pge->ix3 > ovalue)
3633 hs[g->nhs].flags = ST_END;
3634 else
3635 hs[g->nhs].flags = 0;
3636
3637 if( hs[g->nhs].flags != 0 )
3638 g->nhs++;
3639
3640 /* add end as hstem */
3641 hs[g->nhs].value = ge->iy3;
3642 hs[g->nhs].origin
3643 = hs[g->nhs].from
3644 = hs[g->nhs].to = ge->ix3;
3645 hs[g->nhs].ge = ge->frwd;
3646
3647 if(nge->type==GE_CURVE)
3648 ovalue=nge->ix1;
3649 else
3650 ovalue=nge->ix3;
3651
3652 if (ovalue < ge->ix3)
3653 hs[g->nhs].flags = ST_UP | ST_END;
3654 else if (ovalue > ge->ix3)
3655 hs[g->nhs].flags = ST_END;
3656 else
3657 hs[g->nhs].flags = 0;
3658
3659 if( hs[g->nhs].flags != 0 )
3660 g->nhs++;
3661
3662 g->nvs++;
3663 }
3664 /*
3665 * check the end of line for a not smooth local
3666 * extremum
3667 */
3668 nge = ge->frwd;
3669
3670 if (nge == 0)
3671 continue;
3672 else if (nge->type == GE_LINE) {
3673 nx = nge->ix3;
3674 ny = nge->iy3;
3675 } else if (nge->type == GE_CURVE) {
3676 nx = nge->ix1;
3677 ny = nge->iy1;
3678 } else
3679 continue;
3680
3681 /* check for vertical extremums */
3682 if (ge->iy3 > ge->prev->iy3 && ge->iy3 > ny
3683 || ge->iy3 < ge->prev->iy3 && ge->iy3 < ny) {
3684 hs[g->nhs].value = ge->iy3;
3685 hs[g->nhs].from
3686 = hs[g->nhs].to
3687 = hs[g->nhs].origin = ge->ix3;
3688 hs[g->nhs].ge = ge->frwd;
3689
3690 if (ge->ix3 < ge->prev->ix3
3691 || nx < ge->ix3)
3692 hs[g->nhs].flags = ST_UP;
3693 else
3694 hs[g->nhs].flags = 0;
3695
3696 if (ge->ix3 != ge->prev->ix3 || nx != ge->ix3)
3697 g->nhs++;
3698 }
3699 /*
3700 * the same point may be both horizontal and vertical
3701 * extremum
3702 */
3703 /* check for horizontal extremums */
3704 if (ge->ix3 > ge->prev->ix3 && ge->ix3 > nx
3705 || ge->ix3 < ge->prev->ix3 && ge->ix3 < nx) {
3706 vs[g->nvs].value = ge->ix3;
3707 vs[g->nvs].from
3708 = vs[g->nvs].to
3709 = vs[g->nvs].origin = ge->iy3;
3710 vs[g->nvs].ge = ge->frwd;
3711
3712 if (ge->iy3 > ge->prev->iy3
3713 || ny > ge->iy3)
3714 vs[g->nvs].flags = ST_UP;
3715 else
3716 vs[g->nvs].flags = 0;
3717
3718 if (ge->iy3 != ge->prev->iy3 || ny != ge->iy3)
3719 g->nvs++;
3720 }
3721 }
3722 }
3723
3724 g->nhs=addbluestems(hs, g->nhs);
3725 sortstems(hs, g->nhs);
3726 sortstems(vs, g->nvs);
3727
3728 if (ISDBG(STEMS))
3729 debugstems(g->name, hs, g->nhs, vs, g->nvs);
3730
3731 /* find the stems interacting with the Blue Zones */
3732 markbluestems(hs, g->nhs);
3733
3734 if(subhints) {
3735 if (ISDBG(SUBSTEMS))
3736 fprintf(pfa_file, "%% %s: joining subst horizontal stems\n", g->name);
3737 joinsubstems(hs, hs_pairs, g->nhs, 1);
3738 uniformstems(hs, hs_pairs, g->nhs);
3739
3740 if (ISDBG(SUBSTEMS))
3741 fprintf(pfa_file, "%% %s: joining subst vertical stems\n", g->name);
3742 joinsubstems(vs, vs_pairs, g->nvs, 0);
3743
3744 groupsubstems(g, hs, hs_pairs, g->nhs, vs, vs_pairs, g->nvs);
3745 }
3746
3747 if (ISDBG(MAINSTEMS))
3748 fprintf(pfa_file, "%% %s: joining main horizontal stems\n", g->name);
3749 g->nhs = joinmainstems(hs, g->nhs, 1);
3750 if (ISDBG(MAINSTEMS))
3751 fprintf(pfa_file, "%% %s: joining main vertical stems\n", g->name);
3752 g->nvs = joinmainstems(vs, g->nvs, 0);
3753
3754 if (ISDBG(MAINSTEMS))
3755 debugstems(g->name, hs, g->nhs, vs, g->nvs);
3756
3757 if(g->nhs > 0) {
3758 if ((sp = malloc(sizeof(STEM) * g->nhs)) == 0) {
3759 fprintf(stderr, "**** not enough memory for hints ****\n");
3760 exit(255);
3761 }
3762 g->hstems = sp;
3763 memcpy(sp, hs, sizeof(STEM) * g->nhs);
3764 } else
3765 g->hstems = 0;
3766
3767 if(g->nvs > 0) {
3768 if ((sp = malloc(sizeof(STEM) * g->nvs)) == 0) {
3769 fprintf(stderr, "**** not enough memory for hints ****\n");
3770 exit(255);
3771 }
3772 g->vstems = sp;
3773 memcpy(sp, vs, sizeof(STEM) * g->nvs);
3774 } else
3775 g->vstems = 0;
3776
3777 /* now check that the stems won't overflow the interpreter's stem stack:
3778 * some interpreters (like X11) push the stems on each change into
3779 * stack and pop them only after the whole glyphs is completed.
3780 */
3781
3782 totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */
3783 lastgrp = -1;
3784
3785 for (ge = g->entries; ge != 0; ge = ge->next) {
3786 grp=ge->stemid;
3787 if(grp >= 0 && grp != lastgrp) {
3788 if(grp==0)
3789 totals += g->nsbs[0];
3790 else
3791 totals += g->nsbs[grp] - g->nsbs[grp-1];
3792
3793 lastgrp = grp;
3794 }
3795 }
3796
3797 /* be on the safe side, check for >= , not > */
3798 if(totals >= max_stemdepth) { /* oops, too deep */
3799 WARNING_2 {
3800 fprintf(stderr, "Warning: glyph %s needs hint stack depth %d\n", g->name, totals);
3801 fprintf(stderr, " (limit %d): removed the substituted hints from it\n", max_stemdepth);
3802 }
3803 if(g->nsg > 0) {
3804 for (ge = g->entries; ge != 0; ge = ge->next)
3805 ge->stemid = -1;
3806 free(g->sbstems); g->sbstems = 0;
3807 free(g->nsbs); g->nsbs = 0;
3808 g->nsg = 0;
3809 }
3810 }
3811
3812 /* now check if there are too many main stems */
3813 totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */
3814 if(totals >= max_stemdepth) {
3815 /* even worse, too much of non-substituted stems */
3816 WARNING_2 {
3817 fprintf(stderr, "Warning: glyph %s has %d main hints\n", g->name, totals);
3818 fprintf(stderr, " (limit %d): removed the hints from it\n", max_stemdepth);
3819 }
3820 if(g->vstems) {
3821 free(g->vstems); g->vstems = 0; g->nvs = 0;
3822 }
3823 if(g->hstems) {
3824 free(g->hstems); g->hstems = 0; g->nhs = 0;
3825 }
3826 }
3827 }
3828
3829 /* convert weird curves that are close to lines into lines.
3830 */
3831
3832 void
fstraighten(GLYPH * g)3833 fstraighten(
3834 GLYPH * g
3835 )
3836 {
3837 GENTRY *ge, *pge, *nge, *ige;
3838 double df;
3839 int dir;
3840 double iln, oln;
3841 int svdir, i, o;
3842
3843 for (ige = g->entries; ige != 0; ige = ige->next) {
3844 if (ige->type != GE_CURVE)
3845 continue;
3846
3847 ge = ige;
3848 pge = ge->bkwd;
3849 nge = ge->frwd;
3850
3851 df = 0.;
3852
3853 /* look for vertical then horizontal */
3854 for(i=0; i<2; i++) {
3855 o = !i; /* other axis */
3856
3857 iln = fabs(ge->fpoints[i][2] - pge->fpoints[i][2]);
3858 oln = fabs(ge->fpoints[o][2] - pge->fpoints[o][2]);
3859 /*
3860 * if current curve is almost a vertical line, and it
3861 * doesn't begin or end horizontally (and the prev/next
3862 * line doesn't join smoothly ?)
3863 */
3864 if( oln < 1.
3865 || ge->fpoints[o][2] == ge->fpoints[o][1]
3866 || ge->fpoints[o][0] == pge->fpoints[o][2]
3867 || iln > 2.
3868 || iln > 1. && iln/oln > 0.1 )
3869 continue;
3870
3871
3872 if(ISDBG(STRAIGHTEN))
3873 fprintf(stderr,"** straighten almost %s\n", (i? "horizontal":"vertical"));
3874
3875 df = ge->fpoints[i][2] - pge->fpoints[i][2];
3876 dir = fsign(ge->fpoints[o][2] - pge->fpoints[o][2]);
3877 ge->type = GE_LINE;
3878
3879 /*
3880 * suck in all the sequence of such almost lines
3881 * going in the same direction but not deviating
3882 * too far from vertical
3883 */
3884 iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]);
3885 oln = nge->fpoints[o][2] - ge->fpoints[o][2];
3886
3887 while (fabs(df) <= 5 && nge->type == GE_CURVE
3888 && dir == fsign(oln) /* that also gives oln != 0 */
3889 && iln <= 2.
3890 && ( iln <= 1. || iln/fabs(oln) <= 0.1 ) ) {
3891 ge->fx3 = nge->fx3;
3892 ge->fy3 = nge->fy3;
3893
3894 if(ISDBG(STRAIGHTEN))
3895 fprintf(stderr,"** straighten collapsing %s\n", (i? "horizontal":"vertical"));
3896 freethisge(nge);
3897 fixendpath(ge);
3898 pge = ge->bkwd;
3899 nge = ge->frwd;
3900
3901 df = ge->fpoints[i][2] - pge->fpoints[i][2];
3902
3903 iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]);
3904 oln = nge->fpoints[o][2] - ge->fpoints[o][2];
3905 }
3906
3907 /* now check what do we have as previous/next line */
3908
3909 if(ge != pge) {
3910 if( pge->type == GE_LINE && pge->fpoints[i][2] == pge->prev->fpoints[i][2]
3911 && fabs(pge->fpoints[o][2] != pge->prev->fpoints[o][2]) ) {
3912 if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with previous 0x%x 0x%x\n", pge, ge);
3913 /* join the previous line with current */
3914 pge->fx3 = ge->fx3;
3915 pge->fy3 = ge->fy3;
3916
3917 ige = freethisge(ge)->prev; /* keep the iterator valid */
3918 ge = pge;
3919 fixendpath(ge);
3920 pge = ge->bkwd;
3921 }
3922 }
3923
3924 if(ge != nge) {
3925 if (nge->type == GE_LINE && nge->fpoints[i][2] == ge->fpoints[i][2]
3926 && fabs(nge->fpoints[o][2] != ge->fpoints[o][2]) ) {
3927 if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with next 0x%x 0x%x\n", ge, nge);
3928 /* join the next line with current */
3929 ge->fx3 = nge->fx3;
3930 ge->fy3 = nge->fy3;
3931
3932 freethisge(nge);
3933 fixendpath(ge);
3934 pge = ge->bkwd;
3935 nge = ge->frwd;
3936
3937 }
3938 }
3939
3940 if(ge != pge) {
3941 /* try to align the lines if neccessary */
3942 if(df != 0.)
3943 fclosegap(ge, ge, i, df, NULL);
3944 } else {
3945 /* contour consists of only one line, get rid of it */
3946 ige = freethisge(ge); /* keep the iterator valid */
3947 if(ige == 0) /* this was the last contour */
3948 return;
3949 ige = ige->prev;
3950 }
3951
3952 break; /* don't bother looking at the other axis */
3953 }
3954 }
3955 }
3956
3957 /* solve a square equation,
3958 * returns the number of solutions found, the solutions
3959 * are stored in res which should point to array of two doubles.
3960 * min and max limit the area for solutions
3961 */
3962
3963 static int
fsqequation(double a,double b,double c,double * res,double min,double max)3964 fsqequation(
3965 double a,
3966 double b,
3967 double c,
3968 double *res,
3969 double min,
3970 double max
3971 )
3972 {
3973 double D;
3974 int n;
3975
3976 if(ISDBG(SQEQ)) fprintf(stderr, "sqeq(%g,%g,%g) [%g;%g]\n", a, b, c, min, max);
3977
3978 if(fabs(a) < 0.000001) { /* if a linear equation */
3979 n=0;
3980 if(fabs(b) < 0.000001) /* not an equation at all */
3981 return 0;
3982 res[0] = -c/b;
3983 if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: linear t=%g\n", res[0]);
3984 if(res[0] >= min && res[0] <= max)
3985 n++;
3986 return n;
3987 }
3988
3989 D = b*b - 4.0*a*c;
3990 if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: D=%g\n", D);
3991 if(D<0)
3992 return 0;
3993
3994 D = sqrt(D);
3995
3996 n=0;
3997 res[0] = (-b+D) / (2*a);
3998 if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t1=%g\n", res[0]);
3999 if(res[0] >= min && res[0] <= max)
4000 n++;
4001
4002 res[n] = (-b-D) / (2*a);
4003 if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t2=%g\n", res[n]);
4004 if(res[n] >= min && res[n] <= max)
4005 n++;
4006
4007 /* return 2nd solution only if it's different enough */
4008 if(n==2 && fabs(res[0]-res[1])<0.000001)
4009 n=1;
4010
4011 return n;
4012 }
4013
4014 /* check that the curves don't cross quadrant boundary */
4015 /* (float) */
4016
4017 /*
4018 Here we make sure that the curve does not continue past
4019 horizontal or vertical extremums. The horizontal points are
4020 explained, vertical points are by analogy.
4021
4022 The horizontal points are where the derivative
4023 dy/dx is equal to 0. But the Bezier curves are defined by
4024 parametric formulas
4025 x=fx(t)
4026 y=fy(t)
4027 so finding this derivative is complicated.
4028 Also even if we find some point (x,y) splitting at this point
4029 is far not obvious. Fortunately we can use dy/dt = 0 instead,
4030 this gets to a rather simple square equation and splitting
4031 at a known value of t is simple.
4032
4033 The formulas are:
4034
4035 y = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3
4036 y = (-A+3*B-3*C+D)*t^3 + (3*A-6*B+3*C)*t^2 + (-3*A+3*B)*t + A
4037 dy/dt = 3*(-A+3*B-3*C+D)*t^2 + 2*(3*A-6*B+3*C)*t + (-3*A+3*B)
4038 */
4039
4040 void
ffixquadrants(GLYPH * g)4041 ffixquadrants(
4042 GLYPH *g
4043 )
4044 {
4045 GENTRY *ge, *nge;
4046 int i, j, np, oldnp;
4047 double sp[5]; /* split points, last one empty */
4048 char dir[5]; /* for debugging, direction by which split happened */
4049 double a, b, *pts; /* points of a curve */
4050
4051 for (ge = g->entries; ge != 0; ge = ge->next) {
4052 if (ge->type != GE_CURVE)
4053 continue;
4054
4055 doagain:
4056 np = 0; /* no split points yet */
4057 if(ISDBG(QUAD)) {
4058 fprintf(stderr, "%s: trying 0x%x (%g %g) (%g %g) (%g %g) (%g %g)\n ", g->name,
4059 ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2,
4060 ge->fx3, ge->fy3);
4061 }
4062 for(i=0; i<2; i++) { /* first for x then for y */
4063 /* find the cooridnates of control points */
4064 a = ge->prev->fpoints[i][2];
4065 pts = &ge->fpoints[i][0];
4066
4067 oldnp = np;
4068 np += fsqequation(
4069 3.0*(-a + 3.0*pts[0] - 3.0*pts[1] + pts[2]),
4070 6.0*(a - 2.0*pts[0] + pts[1]),
4071 3.0*(-a + pts[0]),
4072 &sp[np],
4073 0.0, 1.0); /* XXX range is [0;1] */
4074
4075 if(np == oldnp)
4076 continue;
4077
4078 if(ISDBG(QUAD))
4079 fprintf(stderr, "%s: 0x%x: %d pts(%c): ",
4080 g->name, ge, np-oldnp, i? 'y':'x');
4081
4082 /* remove points that are too close to the ends
4083 * because hor/vert ends are permitted, also
4084 * if the split point is VERY close to the ends
4085 * but not exactly then just flatten it and check again.
4086 */
4087 for(j = oldnp; j<np; j++) {
4088 dir[j] = i;
4089 if(ISDBG(QUAD))
4090 fprintf(stderr, "%g ", sp[j]);
4091 if(sp[j] < 0.03) { /* front end of curve */
4092 if(ge->fpoints[i][0] != ge->prev->fpoints[i][2]) {
4093 ge->fpoints[i][0] = ge->prev->fpoints[i][2];
4094 if(ISDBG(QUAD)) fprintf(stderr, "flattened at front\n");
4095 goto doagain;
4096 }
4097 if( ge->fpoints[i][1] != ge->fpoints[i][0]
4098 && fsign(ge->fpoints[i][2] - ge->fpoints[i][1])
4099 != fsign(ge->fpoints[i][1] - ge->fpoints[i][0]) ) {
4100 ge->fpoints[i][1] = ge->fpoints[i][0];
4101 if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at front\n");
4102 goto doagain;
4103 }
4104 sp[j] = sp[j+1]; np--; j--;
4105 if(ISDBG(QUAD)) fprintf(stderr, "(front flat) ");
4106 } else if(sp[j] > 0.97) { /* rear end of curve */
4107 if(ge->fpoints[i][1] != ge->fpoints[i][2]) {
4108 ge->fpoints[i][1] = ge->fpoints[i][2];
4109 if(ISDBG(QUAD)) fprintf(stderr, "flattened at rear\n");
4110 goto doagain;
4111 }
4112 if( ge->fpoints[i][0] != ge->fpoints[i][1]
4113 && fsign(ge->prev->fpoints[i][2] - ge->fpoints[i][0])
4114 != fsign(ge->fpoints[i][0] - ge->fpoints[i][1]) ) {
4115 ge->fpoints[i][0] = ge->fpoints[i][1];
4116 if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at rear\n");
4117 goto doagain;
4118 }
4119 sp[j] = sp[j+1]; np--; j--;
4120 if(ISDBG(QUAD)) fprintf(stderr, "(rear flat) ");
4121 }
4122 }
4123 if(ISDBG(QUAD)) fprintf(stderr, "\n");
4124 }
4125
4126 if(np==0) /* no split points, leave it alone */
4127 continue;
4128
4129 if(ISDBG(QUAD)) {
4130 fprintf(stderr, "%s: splitting 0x%x (%g %g) (%g %g) (%g %g) (%g %g) at %d points\n ", g->name,
4131 ge, ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2,
4132 ge->fx3, ge->fy3, np);
4133 for(i=0; i<np; i++)
4134 fprintf(stderr, "%g(%c) ", sp[i], dir[i] ? 'y':'x');
4135 fprintf(stderr, "\n");
4136 }
4137
4138 /* sort the points ascending */
4139 for(i=0; i<np; i++)
4140 for(j=i+1; j<np; j++)
4141 if(sp[i] > sp[j]) {
4142 a = sp[i]; sp[i] = sp[j]; sp[j] = a;
4143 }
4144
4145 /* now finally do the split on each point */
4146 for(j=0; j<np; j++) {
4147 double k1, k2, c;
4148
4149 k1 = sp[j];
4150 k2 = 1 - k1;
4151
4152 if(ISDBG(QUAD)) fprintf(stderr, " 0x%x %g/%g\n", ge, k1, k2);
4153
4154 nge = newgentry(GEF_FLOAT);
4155 (*nge) = (*ge);
4156
4157 #define SPLIT(pt1, pt2) ( (pt1) + k1*((pt2)-(pt1)) ) /* order is important! */
4158 for(i=0; i<2; i++) { /* for x and y */
4159 a = ge->fpoints[i][0]; /* get the middle points */
4160 b = ge->fpoints[i][1];
4161
4162 /* calculate new internal points */
4163 c = SPLIT(a, b);
4164
4165 ge->fpoints[i][0] = SPLIT(ge->prev->fpoints[i][2], a);
4166 ge->fpoints[i][1] = SPLIT(ge->fpoints[i][0], c);
4167
4168 nge->fpoints[i][1] = SPLIT(b, nge->fpoints[i][2]);
4169 nge->fpoints[i][0] = SPLIT(c, nge->fpoints[i][1]);
4170
4171 ge->fpoints[i][2] = SPLIT(ge->fpoints[i][1],
4172 + nge->fpoints[i][0]);
4173 }
4174 #undef SPLIT
4175
4176 addgeafter(ge, nge);
4177
4178 /* go to the next part, adjust remaining points */
4179 ge = nge;
4180 for(i=j+1; i<np; i++)
4181 sp[i] = (sp[i]-k1) / k2;
4182 }
4183 }
4184
4185 }
4186
4187 /* check if a curve is a zigzag */
4188
4189 static int
iiszigzag(GENTRY * ge)4190 iiszigzag(
4191 GENTRY *ge
4192 )
4193 {
4194 double k, k1, k2;
4195 int a, b;
4196
4197 if (ge->type != GE_CURVE)
4198 return 0;
4199
4200 a = ge->iy2 - ge->iy1;
4201 b = ge->ix2 - ge->ix1;
4202 if(a == 0) {
4203 if(b == 0) {
4204 return 0;
4205 } else
4206 k = FBIGVAL;
4207 } else
4208 k = fabs((double) b / (double) a);
4209
4210 a = ge->iy1 - ge->prev->iy3;
4211 b = ge->ix1 - ge->prev->ix3;
4212 if(a == 0) {
4213 if(b == 0) {
4214 return 0;
4215 } else
4216 k1 = FBIGVAL;
4217 } else
4218 k1 = fabs((double) b / (double) a);
4219
4220 a = ge->iy3 - ge->iy2;
4221 b = ge->ix3 - ge->ix2;
4222 if(a == 0) {
4223 if(b == 0) {
4224 return 0;
4225 } else
4226 k2 = FBIGVAL;
4227 } else
4228 k2 = fabs((double) b / (double) a);
4229
4230 /* if the curve is not a zigzag */
4231 if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k)
4232 return 0;
4233 else
4234 return 1;
4235 }
4236
4237 /* check if a curve is a zigzag - floating */
4238
4239 static int
fiszigzag(GENTRY * ge)4240 fiszigzag(
4241 GENTRY *ge
4242 )
4243 {
4244 double k, k1, k2;
4245 double a, b;
4246
4247 if (ge->type != GE_CURVE)
4248 return 0;
4249
4250 a = fabs(ge->fy2 - ge->fy1);
4251 b = fabs(ge->fx2 - ge->fx1);
4252 if(a < FEPS) {
4253 if(b < FEPS) {
4254 return 0;
4255 } else
4256 k = FBIGVAL;
4257 } else
4258 k = b / a;
4259
4260 a = fabs(ge->fy1 - ge->prev->fy3);
4261 b = fabs(ge->fx1 - ge->prev->fx3);
4262 if(a < FEPS) {
4263 if(b < FEPS) {
4264 return 0;
4265 } else
4266 k1 = FBIGVAL;
4267 } else
4268 k1 = b / a;
4269
4270 a = fabs(ge->fy3 - ge->fy2);
4271 b = fabs(ge->fx3 - ge->fx2);
4272 if(a < FEPS) {
4273 if(b < FEPS) {
4274 return 0;
4275 } else
4276 k2 = FBIGVAL;
4277 } else
4278 k2 = b / a;
4279
4280 /* if the curve is not a zigzag */
4281 if (k1+0.0001 >= k && k2 <= k+0.0001 || k1 <= k+0.0001 && k2+0.0001 >= k)
4282 return 0;
4283 else
4284 return 1;
4285 }
4286
4287 /* split the zigzag-like curves into two parts */
4288
4289 void
fsplitzigzags(GLYPH * g)4290 fsplitzigzags(
4291 GLYPH * g
4292 )
4293 {
4294 GENTRY *ge, *nge;
4295 double a, b, c, d;
4296
4297 assertisfloat(g, "splitting zigzags");
4298 for (ge = g->entries; ge != 0; ge = ge->next) {
4299 if (ge->type != GE_CURVE)
4300 continue;
4301
4302 /* if the curve is not a zigzag */
4303 if ( !fiszigzag(ge) ) {
4304 continue;
4305 }
4306
4307 if(ISDBG(FCONCISE)) {
4308 double maxsc1, maxsc2;
4309 fprintf(stderr, "split a zigzag ");
4310 fnormalizege(ge);
4311 if( fcrossraysge(ge, ge, &maxsc1, &maxsc2, NULL) ) {
4312 fprintf(stderr, "sc1=%g sc2=%g\n", maxsc1, maxsc2);
4313 } else {
4314 fprintf(stderr, "(rays don't cross)\n");
4315 }
4316 }
4317 /* split the curve by t=0.5 */
4318 nge = newgentry(GEF_FLOAT);
4319 (*nge) = (*ge);
4320 nge->type = GE_CURVE;
4321
4322 a = ge->prev->fx3;
4323 b = ge->fx1;
4324 c = ge->fx2;
4325 d = ge->fx3;
4326 nge->fx3 = d;
4327 nge->fx2 = (c + d) / 2.;
4328 nge->fx1 = (b + 2. * c + d) / 4.;
4329 ge->fx3 = (a + b * 3. + c * 3. + d) / 8.;
4330 ge->fx2 = (a + 2. * b + c) / 4.;
4331 ge->fx1 = (a + b) / 2.;
4332
4333 a = ge->prev->fy3;
4334 b = ge->fy1;
4335 c = ge->fy2;
4336 d = ge->fy3;
4337 nge->fy3 = d;
4338 nge->fy2 = (c + d) / 2.;
4339 nge->fy1 = (b + 2. * c + d) / 4.;
4340 ge->fy3 = (a + b * 3. + c * 3. + d) / 8.;
4341 ge->fy2 = (a + 2. * b + c) / 4.;
4342 ge->fy1 = (a + b) / 2.;
4343
4344 addgeafter(ge, nge);
4345
4346 if(ISDBG(FCONCISE)) {
4347 dumppaths(g, ge, nge);
4348 }
4349 }
4350 }
4351
4352 /* free this GENTRY, returns what was ge->next
4353 * (ge must be of type GE_LINE or GE_CURVE)
4354 * works on both float and int entries
4355 */
4356
4357 static GENTRY *
freethisge(GENTRY * ge)4358 freethisge(
4359 GENTRY *ge
4360 )
4361 {
4362 GENTRY *xge;
4363
4364 if (ge->bkwd != ge->prev) {
4365 /* at beginning of the contour */
4366
4367 xge = ge->bkwd;
4368 if(xge == ge) { /* was the only line in contour */
4369 /* remove the contour completely */
4370 /* prev is GE_MOVE, next is GE_PATH, remove them all */
4371
4372 /* may be the first contour, then ->bkwd points to ge->entries */
4373 if(ge->prev->prev == 0)
4374 *(GENTRY **)(ge->prev->bkwd) = ge->next->next;
4375 else
4376 ge->prev->prev->next = ge->next->next;
4377
4378 if(ge->next->next) {
4379 ge->next->next->prev = ge->prev->prev;
4380 ge->next->next->bkwd = ge->prev->bkwd;
4381 }
4382
4383 xge = ge->next->next;
4384 free(ge->prev); free(ge->next); free(ge);
4385 return xge;
4386 }
4387
4388 /* move the start point of the contour */
4389 if(ge->flags & GEF_FLOAT) {
4390 ge->prev->fx3 = xge->fx3;
4391 ge->prev->fy3 = xge->fy3;
4392 } else {
4393 ge->prev->ix3 = xge->ix3;
4394 ge->prev->iy3 = xge->iy3;
4395 }
4396 } else if(ge->frwd != ge->next) {
4397 /* at end of the contour */
4398
4399 xge = ge->frwd->prev;
4400 /* move the start point of the contour */
4401 if(ge->flags & GEF_FLOAT) {
4402 xge->fx3 = ge->bkwd->fx3;
4403 xge->fy3 = ge->bkwd->fy3;
4404 } else {
4405 xge->ix3 = ge->bkwd->ix3;
4406 xge->iy3 = ge->bkwd->iy3;
4407 }
4408 }
4409
4410 ge->prev->next = ge->next;
4411 ge->next->prev = ge->prev;
4412 ge->bkwd->frwd = ge->frwd;
4413 ge->frwd->bkwd = ge->bkwd;
4414
4415 xge = ge->next;
4416 free(ge);
4417 return xge;
4418 }
4419
4420 /* inserts a new gentry (LINE or CURVE) after another (MOVE
4421 * or LINE or CURVE)
4422 * corrects the first GE_MOVE if neccessary
4423 */
4424
4425 static void
addgeafter(GENTRY * oge,GENTRY * nge)4426 addgeafter(
4427 GENTRY *oge, /* after this */
4428 GENTRY *nge /* insert this */
4429 )
4430 {
4431 if(oge->type == GE_MOVE) {
4432 /* insert before next */
4433 if(oge->next->type == GE_PATH) {
4434 /* first and only GENTRY in path */
4435 nge->frwd = nge->bkwd = nge;
4436 } else {
4437 nge->frwd = oge->next;
4438 nge->bkwd = oge->next->bkwd;
4439 oge->next->bkwd->frwd = nge;
4440 oge->next->bkwd = nge;
4441 }
4442 } else {
4443 nge->frwd = oge->frwd;
4444 nge->bkwd = oge;
4445 oge->frwd->bkwd = nge;
4446 oge->frwd = nge;
4447 }
4448
4449 nge->next = oge->next;
4450 nge->prev = oge;
4451 oge->next->prev = nge;
4452 oge->next = nge;
4453
4454 if(nge->frwd->prev->type == GE_MOVE) {
4455 /* fix up the GE_MOVE entry */
4456 if(nge->flags & GEF_FLOAT) {
4457 nge->frwd->prev->fx3 = nge->fx3;
4458 nge->frwd->prev->fy3 = nge->fy3;
4459 } else {
4460 nge->frwd->prev->ix3 = nge->ix3;
4461 nge->frwd->prev->iy3 = nge->iy3;
4462 }
4463 }
4464 }
4465
4466 /*
4467 * Check if this GENTRY happens to be at the end of path
4468 * and fix the first MOVETO accordingly
4469 * handles both int and float
4470 */
4471
4472 static void
fixendpath(GENTRY * ge)4473 fixendpath(
4474 GENTRY *ge
4475 )
4476 {
4477 GENTRY *mge;
4478
4479 mge = ge->frwd->prev;
4480 if(mge->type == GE_MOVE) {
4481 if(ge->flags & GEF_FLOAT) {
4482 mge->fx3 = ge->fx3;
4483 mge->fy3 = ge->fy3;
4484 } else {
4485 mge->ix3 = ge->ix3;
4486 mge->iy3 = ge->iy3;
4487 }
4488 }
4489 }
4490
4491 /*
4492 * This function adjusts the rest of path (the part from...to is NOT changed)
4493 * to cover the specified gap by the specified axis (0 - X, 1 - Y).
4494 * Gap is counted in direction (end_of_to - beginning_of_from).
4495 * Returns by how much the gap was not closed (0.0 if it was fully closed).
4496 * Ret contains by how much the first and last points of [from...to]
4497 * were moved to bring them in consistence to the rest of the path.
4498 * If ret==NULL then this info is not returned.
4499 */
4500
4501 static double
fclosegap(GENTRY * from,GENTRY * to,int axis,double gap,double * ret)4502 fclosegap(
4503 GENTRY *from,
4504 GENTRY *to,
4505 int axis,
4506 double gap,
4507 double *ret
4508 )
4509 {
4510 #define TIMESLARGER 10. /* how many times larger must be a curve to not change too much */
4511 double rm[2];
4512 double oldpos[2];
4513 double times, limit, df, dx;
4514 int j, k;
4515 GENTRY *xge, *pge, *nge, *bge[2];
4516
4517 /* remember the old points to calculate ret */
4518 oldpos[0] = from->prev->fpoints[axis][2];
4519 oldpos[1] = to->fpoints[axis][2];
4520
4521 rm[0] = rm[1] = gap / 2. ;
4522
4523 bge[0] = from; /* this is convenient for iterations */
4524 bge[1] = to;
4525
4526 /* first try to modify large curves but if have none then settle for small */
4527 for(times = (TIMESLARGER-1); times > 0.1; times /= 2. ) {
4528
4529 if(rm[0]+rm[1] == 0.)
4530 break;
4531
4532 /* iterate in both directions, backwards then forwards */
4533 for(j = 0; j<2; j++) {
4534
4535 if(rm[j] == 0.) /* if this direction is exhausted */
4536 continue;
4537
4538 limit = fabs(rm[j]) * (1.+times);
4539
4540 for(xge = bge[j]->cntr[j]; xge != bge[!j]; xge = xge->cntr[j]) {
4541 dx = xge->fpoints[axis][2] - xge->prev->fpoints[axis][2];
4542 df = fabs(dx) - limit;
4543 if( df <= FEPS ) /* curve is too small to change */
4544 continue;
4545
4546 if( df >= fabs(rm[j]) )
4547 df = rm[j];
4548 else
4549 df *= fsign(rm[j]); /* we may cover this part of rm */
4550
4551 rm[j] -= df;
4552 limit = fabs(rm[j]) * (1.+times);
4553
4554 if(xge->type == GE_CURVE) { /* correct internal points */
4555 double scale = ((dx+df) / dx) - 1.;
4556 double base;
4557
4558 if(j)
4559 base = xge->fpoints[axis][2];
4560 else
4561 base = xge->prev->fpoints[axis][2];
4562
4563 for(k = 0; k<2; k++)
4564 xge->fpoints[axis][k] += scale *
4565 (xge->fpoints[axis][k] - base);
4566 }
4567
4568 /* move all the intermediate lines */
4569 if(j) {
4570 df = -df; /* absolute direction */
4571 pge = bge[1]->bkwd;
4572 nge = xge->bkwd;
4573 } else {
4574 xge->fpoints[axis][2] += df;
4575 pge = bge[0];
4576 nge = xge->frwd;
4577 }
4578 while(nge != pge) {
4579 if(nge->type == GE_CURVE) {
4580 nge->fpoints[axis][0] +=df;
4581 nge->fpoints[axis][1] +=df;
4582 }
4583 nge->fpoints[axis][2] += df;
4584 if(nge->next != nge->frwd) { /* last entry of contour */
4585 nge->frwd->prev->fpoints[axis][2] += df;
4586 }
4587 nge = nge->cntr[!j];
4588 }
4589
4590 if(rm[j] == 0.)
4591 break;
4592 }
4593 }
4594 }
4595
4596 /* find the difference */
4597 oldpos[0] -= from->prev->fpoints[axis][2];
4598 oldpos[1] -= to->fpoints[axis][2];
4599
4600 if(ret) {
4601 ret[0] = oldpos[0] - from->prev->fpoints[axis][2];
4602 ret[1] = oldpos[1] - to->fpoints[axis][2];
4603 }
4604
4605 #if 0
4606 if( rm[0]+rm[1] != gap - oldpos[1] + oldpos[0]) {
4607 fprintf(stderr, "** gap=%g rm[0]=%g rm[1]=%g o[0]=%g o[1]=%g rg=%g og=%g\n",
4608 gap, rm[0], rm[1], oldpos[0], oldpos[1], rm[0]+rm[1],
4609 gap - oldpos[1] + oldpos[0]);
4610 }
4611 #endif
4612
4613 return rm[0]+rm[1];
4614 #undef TIMESLARGER
4615 }
4616
4617 /* remove the lines or curves smaller or equal to the size limit */
4618
4619 static void
fdelsmall(GLYPH * g,double minlen)4620 fdelsmall(
4621 GLYPH *g,
4622 double minlen
4623 )
4624 {
4625 GENTRY *ge, *nge, *pge, *xge, *next;
4626 int i, k;
4627 double dx, dy, d2, d2m;
4628 double minlen2;
4629 #define TIMESLARGER 10. /* how much larger must be a curve to not change too much */
4630
4631 minlen2 = minlen*minlen;
4632
4633 for (ge = g->entries; ge != 0; ge = next) {
4634 next = ge->next;
4635
4636 if (ge->type != GE_CURVE && ge->type != GE_LINE)
4637 continue;
4638
4639 d2m = 0;
4640 for(i= (ge->type==GE_CURVE? 0: 2); i<3; i++) {
4641 dx = ge->fxn[i] - ge->prev->fx3;
4642 dy = ge->fyn[i] - ge->prev->fy3;
4643 d2 = dx*dx + dy*dy;
4644 if(d2m < d2)
4645 d2m = d2;
4646 }
4647
4648 if( d2m > minlen2 ) { /* line is not too small */
4649 /* XXX add more normalization here */
4650 continue;
4651 }
4652
4653 /* if the line is too small */
4654
4655 /* check forwards if we have a whole sequence of them */
4656 nge = ge;
4657 for(xge = ge->frwd; xge != ge; xge = xge->frwd) {
4658 d2m = 0;
4659 for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) {
4660 dx = xge->fxn[i] - xge->prev->fx3;
4661 dy = xge->fyn[i] - xge->prev->fy3;
4662 d2 = dx*dx + dy*dy;
4663 if(d2m < d2)
4664 d2m = d2;
4665 }
4666 if( d2m > minlen2 ) /* line is not too small */
4667 break;
4668 nge = xge;
4669 if(next == nge) /* move the next step past this sequence */
4670 next = next->next;
4671 }
4672
4673 /* check backwards if we have a whole sequence of them */
4674 pge = ge;
4675 for(xge = ge->bkwd; xge != ge; xge = xge->bkwd) {
4676 d2m = 0;
4677 for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) {
4678 dx = xge->fxn[i] - xge->prev->fx3;
4679 dy = xge->fyn[i] - xge->prev->fy3;
4680 d2 = dx*dx + dy*dy;
4681 if(d2m < d2)
4682 d2m = d2;
4683 }
4684 if( d2m > minlen2 ) /* line is not too small */
4685 break;
4686 pge = xge;
4687 }
4688
4689 /* now we have a sequence of small fragments in pge...nge (inclusive) */
4690
4691 if(ISDBG(FCONCISE)) {
4692 fprintf(stderr, "glyph %s has very small fragments(%x..%x..%x)\n",
4693 g->name, pge, ge, nge);
4694 dumppaths(g, pge, nge);
4695 }
4696
4697 /* reduce whole sequence to one part and remember the middle point */
4698 if(pge != nge) {
4699 while(1) {
4700 xge = pge->frwd;
4701 if(xge == nge) {
4702 pge->fx1 = pge->fx2 = pge->fx3;
4703 pge->fx3 = nge->fx3;
4704 pge->fy1 = pge->fy2 = pge->fy3;
4705 pge->fy3 = nge->fy3;
4706 pge->type = GE_CURVE;
4707 freethisge(nge);
4708 break;
4709 }
4710 if(xge == nge->bkwd) {
4711 pge->fx1 = pge->fx2 = (pge->fx3+xge->fx3)/2.;
4712 pge->fx3 = nge->fx3;
4713 pge->fy1 = pge->fy2 = (pge->fy3+xge->fy3)/2.;
4714 pge->fy3 = nge->fy3;
4715 pge->type = GE_CURVE;
4716 freethisge(nge);
4717 freethisge(xge);
4718 break;
4719 }
4720 freethisge(pge); pge = xge;
4721 xge = nge->bkwd; freethisge(nge); nge = xge;
4722 }
4723 }
4724 ge = pge;
4725
4726 /* check if the whole sequence is small */
4727 dx = ge->fx3 - ge->prev->fx3;
4728 dy = ge->fy3 - ge->prev->fy3;
4729 d2 = dx*dx + dy*dy;
4730
4731 if( d2 > minlen2 ) { /* no, it is not */
4732 double b, d;
4733
4734 WARNING_3 fprintf(stderr, "glyph %s had a sequence of fragments < %g points each, reduced to one curve\n",
4735 g->name, minlen);
4736
4737 /* check that we did not create a monstrosity spanning quadrants */
4738 if(fsign(ge->fx1 - ge->prev->fx1) * fsign(ge->fx3 - ge->fx1) < 0
4739 || fsign(ge->fy1 - ge->prev->fy1) * fsign(ge->fy3 - ge->fy1) < 0 ) {
4740 /* yes, we did; are both parts of this thing big enough ? */
4741 dx = ge->fx1 - ge->prev->fx3;
4742 dy = ge->fy1 - ge->prev->fy3;
4743 d2 = dx*dx + dy*dy;
4744
4745 dx = ge->fx3 - ge->fx1;
4746 dy = ge->fy3 - ge->fy1;
4747 d2m = dx*dx + dy*dy;
4748
4749 if(d2 > minlen2 && d2m > minlen2) { /* make two straights */
4750 nge = newgentry(GEF_FLOAT);
4751 *nge = *ge;
4752
4753 for(i=0; i<2; i++) {
4754 ge->fpoints[i][2] = ge->fpoints[i][0];
4755 b = nge->fpoints[i][0];
4756 d = nge->fpoints[i][2] - b;
4757 nge->fpoints[i][0] = b + 0.1*d;
4758 nge->fpoints[i][1] = b + 0.9*d;
4759 }
4760 }
4761 for(i=0; i<2; i++) { /* make one straight or first of two straights */
4762 b = ge->prev->fpoints[i][2];
4763 d = ge->fpoints[i][2] - b;
4764 ge->fpoints[i][0] = b + 0.1*d;
4765 ge->fpoints[i][1] = b + 0.9*d;
4766 }
4767 }
4768 continue;
4769 }
4770
4771 if(ge->frwd == ge) { /* points to itself, just remove the path completely */
4772 WARNING_3 fprintf(stderr, "glyph %s had a path made of fragments < %g points each, removed\n",
4773 g->name, minlen);
4774
4775 next = freethisge(ge);
4776 continue;
4777 }
4778
4779 /* now close the gap by x and y */
4780 for(i=0; i<2; i++) {
4781 double gap;
4782
4783 gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2];
4784 if( fclosegap(ge, ge, i, gap, NULL) != 0.0 ) {
4785 double scale, base;
4786
4787 /* not good, as the last resort just scale the next line */
4788 gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2];
4789
4790 if(ISDBG(FCONCISE))
4791 fprintf(stderr, " last resort on %c: closing next by %g\n",
4792 (i==0 ? 'x' : 'y'), gap);
4793
4794 nge = ge->frwd;
4795 base = nge->fpoints[i][2];
4796 dx = ge->fpoints[i][2] - base;
4797 if(fabs(dx) < FEPS)
4798 continue;
4799
4800 scale = ((dx-gap) / dx);
4801
4802 if(nge->type == GE_CURVE)
4803 for(k = 0; k<2; k++)
4804 nge->fpoints[i][k] = base +
4805 scale * (nge->fpoints[i][k] - base);
4806
4807 ge->fpoints[i][2] -= gap;
4808 }
4809 }
4810
4811 /* OK, the gap is closed - remove this useless GENTRY */
4812 freethisge(ge);
4813 }
4814 #undef TIMESLARGER
4815 }
4816
4817 /* find the point where two rays continuing vectors cross
4818 * returns 1 if they cross, 0 if they don't
4819 * If they cross optionally (if the pointers are not NULL) returns
4820 * the maximal scales for both vectors and also optionally the point
4821 * where the rays cross (twice).
4822 * Expects that the curves are normalized.
4823 *
4824 * For convenience there are 2 front-end functions taking
4825 * arguments in different formats
4826 */
4827
4828 struct ray {
4829 double x1, y1, x2, y2;
4830 int isvert;
4831 double k, b; /* lines are represented as y = k*x + b */
4832 double *maxp;
4833 };
4834 static struct ray ray[3];
4835
4836 /* the back-end doing the actual work
4837 * the rays are defined in the static array ray[]
4838 */
4839
4840 static int
fcrossraysxx(double crossdot[2][2])4841 fcrossraysxx(
4842 double crossdot[2][2]
4843 )
4844 {
4845 double x, y, max;
4846 int i;
4847
4848 for(i=0; i<2; i++) {
4849 if(ray[i].x1 == ray[i].x2)
4850 ray[i].isvert = 1;
4851 else {
4852 ray[i].isvert = 0;
4853 ray[i].k = (ray[i].y2 - ray[i].y1) / (ray[i].x2 - ray[i].x1);
4854 ray[i].b = ray[i].y2 - ray[i].k * ray[i].x2;
4855 }
4856 }
4857
4858 if(ray[0].isvert && ray[1].isvert) {
4859 if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: both vertical\n");
4860 return 0; /* both vertical, don't cross */
4861 }
4862
4863 if(ray[1].isvert) {
4864 ray[2] = ray[0]; /* exchange them */
4865 ray[0] = ray[1];
4866 ray[1] = ray[2];
4867 }
4868
4869 if(ray[0].isvert) {
4870 x = ray[0].x1;
4871 } else {
4872 if( fabs(ray[0].k - ray[1].k) < FEPS) {
4873 if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: parallel lines, k = %g, %g\n",
4874 ray[0].k, ray[1].k);
4875 return 0; /* parallel lines */
4876 }
4877 x = (ray[1].b - ray[0].b) / (ray[0].k - ray[1].k) ;
4878 }
4879 y = ray[1].k * x + ray[1].b;
4880
4881 for(i=0; i<2; i++) {
4882 if(ray[i].isvert)
4883 max = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1);
4884 else
4885 max = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1);
4886 /* check if wrong sides of rays cross */
4887 if( max < 0 ) {
4888 if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: %c scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n",
4889 (i?'Y':'X'), max, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1);
4890 return 0;
4891 }
4892 if(ray[i].maxp)
4893 *ray[i].maxp = max;
4894 }
4895 if(crossdot != 0) {
4896 crossdot[0][0] = crossdot[1][0] = x;
4897 crossdot[0][1] = crossdot[1][1] = y;
4898 }
4899 return 1;
4900 }
4901
4902 /* the front-end getting the arguments from 4 dots defining
4903 * a curve in the same format as for fapproxcurve():
4904 * rays are defined as beginning and end of the curve,
4905 * the crossdot is inserted as the two middle dots of the curve
4906 */
4907
4908 int
fcrossrayscv(double curve[4][2],double * max1,double * max2)4909 fcrossrayscv(
4910 double curve[4][2 /*X,Y*/],
4911 double *max1,
4912 double *max2
4913 )
4914 {
4915 ray[0].x1 = curve[0][X];
4916 ray[0].y1 = curve[0][Y];
4917 ray[0].x2 = curve[1][X];
4918 ray[0].y2 = curve[1][Y];
4919 ray[0].maxp = max1;
4920
4921 ray[1].x1 = curve[2][X];
4922 ray[1].y1 = curve[2][Y];
4923 ray[1].x2 = curve[3][X];
4924 ray[1].y2 = curve[3][Y];
4925 ray[1].maxp = max2;
4926
4927 return fcrossraysxx(&curve[1]);
4928 }
4929
4930 /* the front-end getting the arguments from gentries:
4931 * rays are defined as beginning of curve1 and end of curve 2
4932 */
4933
4934 int
fcrossraysge(GENTRY * ge1,GENTRY * ge2,double * max1,double * max2,double crossdot[2][2])4935 fcrossraysge(
4936 GENTRY *ge1,
4937 GENTRY *ge2,
4938 double *max1,
4939 double *max2,
4940 double crossdot[2][2]
4941 )
4942 {
4943 ray[0].x1 = ge1->prev->fx3;
4944 ray[0].y1 = ge1->prev->fy3;
4945 ray[0].x2 = ge1->fpoints[X][ge1->ftg];
4946 ray[0].y2 = ge1->fpoints[Y][ge1->ftg];
4947 ray[0].maxp = max1;
4948
4949 ray[1].x1 = ge2->fx3;
4950 ray[1].y1 = ge2->fy3;
4951 if(ge2->rtg < 0) {
4952 ray[1].x2 = ge2->prev->fx3;
4953 ray[1].y2 = ge2->prev->fy3;
4954 } else {
4955 ray[1].x2 = ge2->fpoints[X][ge2->rtg];
4956 ray[1].y2 = ge2->fpoints[Y][ge2->rtg];
4957 }
4958 ray[1].maxp = max2;
4959
4960 return fcrossraysxx(crossdot);
4961 }
4962
4963 /* debugging printout functions */
4964
4965 #if defined(DEBUG_DOTSEG) || defined(DEBUG_DOTCURVE) || defined(DEBUG_APPROXCV)
4966
4967 /* for debugging */
4968 static
printdot(double dot[2])4969 printdot(
4970 double dot[2]
4971 )
4972 {
4973 fprintf(stderr, "(%g,%g)", dot[0], dot[1]);
4974 }
4975
4976 static
printseg(double seg[2][2])4977 printseg(
4978 double seg[2][2]
4979 )
4980 {
4981 putc('[', stderr);
4982 printdot(seg[0]);
4983 putc(' ', stderr);
4984 printdot(seg[1]);
4985 putc(']', stderr);
4986 }
4987
4988 #endif /* DEBUG_* */
4989
4990 /*
4991 * Find squared distance from a dot to a line segment
4992 */
4993
4994 double
fdotsegdist2(double seg[2][2],double dot[2])4995 fdotsegdist2(
4996 double seg[2][2 /*X,Y*/],
4997 double dot[2 /*X,Y*/]
4998 )
4999 {
5000 #define x1 seg[0][X]
5001 #define y1 seg[0][Y]
5002 #define x2 seg[1][X]
5003 #define y2 seg[1][Y]
5004 #define xdot dot[X]
5005 #define ydot dot[Y]
5006
5007 double dx, dy; /* segment dimensions */
5008 double kline, bline; /* segment line formula is y=k*x+b */
5009 double kperp, bperp; /* perpendicular from the dot to the line */
5010 double xcross, ycross; /* where the perpendicular crosses the segment */
5011
5012 /* handle the situation where the nearest point of the segment is its end */
5013 #define HANDLE_LIMITS(less12, lesscr1, lesscr2) \
5014 if( less12 ) { \
5015 if( lesscr1 ) { \
5016 xcross = x1; \
5017 ycross = y1; \
5018 } else if( !(lesscr2) ) { \
5019 xcross = x2; \
5020 ycross = y2; \
5021 } \
5022 } else { \
5023 if( !(lesscr1) ) { \
5024 xcross = x1; \
5025 ycross = y1; \
5026 } else if( lesscr2 ) { \
5027 xcross = x2; \
5028 ycross = y2; \
5029 } \
5030 } \
5031 /* end of macro */
5032
5033
5034 dx = x2 - x1;
5035 dy = y2 - y1;
5036
5037 if(fabs(dx) < FEPS) {
5038 /* special case - vertical line */
5039 #ifdef DEBUG_DOTSEG
5040 printf("vertical line!\n");
5041 #endif
5042 xcross = x1;
5043 ycross = ydot;
5044 HANDLE_LIMITS( y1 < y2, ycross < y1, ycross < y2);
5045 } else if(fabs(dy) < FEPS) {
5046 /* special case - horizontal line */
5047 #ifdef DEBUG_DOTSEG
5048 printf("horizontal line!\n");
5049 #endif
5050 xcross = xdot;
5051 ycross = y1;
5052 HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2)
5053 } else {
5054 kline = dy/dx;
5055 bline = y1 - x1*kline;
5056 kperp = -1./kline;
5057 bperp = ydot - xdot*kperp;
5058
5059 xcross = (bline-bperp) / (kperp-kline);
5060 ycross = kline*xcross + bline;
5061
5062 HANDLE_LIMITS( x1 < x2, xcross < x1, xcross < x2)
5063 }
5064 #ifdef DEBUG_DOTSEG
5065 printf("crossover at (%g,%g)\n", xcross, ycross);
5066 #endif
5067
5068 dx = xdot-xcross;
5069 dy = ydot-ycross;
5070 return dx*dx+dy*dy;
5071 #undef x1
5072 #undef y1
5073 #undef x2
5074 #undef y2
5075 #undef xdot
5076 #undef ydot
5077 #undef HANDLE_LIMITS
5078 }
5079
5080 /* find the weighted quadratic average for the distance of a set
5081 * of dots from the curve; also fills out the individual distances
5082 * for each dot; if maxp!=NULL then returns the maximal squared
5083 * distance in there
5084 */
5085
5086 double
fdotcurvdist2(double curve[4][2],struct dot_dist * dots,int ndots,double * maxp)5087 fdotcurvdist2(
5088 double curve[4][2 /*X,Y*/ ],
5089 struct dot_dist *dots,
5090 int ndots, /* number of entries in dots */
5091 double *maxp
5092 )
5093 {
5094 /* a curve is approximated by this many straight segments */
5095 #define NAPSECT 16
5096 /* a curve is divided into this many sections with equal weight each */
5097 #define NWSECT 4
5098 /* table of coefficients for finding the dots on the curve */
5099 /* tt[0] is left unused */
5100 static double tt[NAPSECT][4];
5101 static int havett = 0; /* flag: tt is initialized */
5102 /* dots on the curve */
5103 double cvd[NAPSECT+1][2 /*X,Y*/];
5104 /* sums by sections */
5105 double sum[NWSECT];
5106 /* counts by sections */
5107 double count[NWSECT];
5108 int d, i, j;
5109 int id1, id2;
5110 double dist1, dist2, dist3, dx, dy, x, y;
5111 double max = 0.;
5112
5113 if(!havett) {
5114 double t, nt, t2, nt2, step;
5115
5116 havett++;
5117 step = 1. / NAPSECT;
5118 t = 0;
5119 for(i=1; i<NAPSECT; i++) {
5120 t += step;
5121 nt = 1 - t;
5122 t2 = t*t;
5123 nt2 = nt*nt;
5124 tt[i][0] = nt2*nt; /* (1-t)^3 */
5125 tt[i][1] = 3*nt2*t; /* 3*(1-t)^2*t */
5126 tt[i][2] = 3*nt*t2; /* 3*(1-t)*t^2 */
5127 tt[i][3] = t2*t; /* t^3 */
5128 }
5129 }
5130
5131 for(i=0; i<NWSECT; i++) {
5132 sum[i] = 0.;
5133 count[i] = 0;
5134 }
5135
5136 /* split the curve into segments */
5137 for(d=0; d<2; d++) { /* X and Y */
5138 cvd[0][d] = curve[0][d]; /* endpoints */
5139 cvd[NAPSECT][d] = curve[3][d];
5140 for(i=1; i<NAPSECT; i++) {
5141 cvd[i][d] = curve[0][d] * tt[i][0]
5142 + curve[1][d] * tt[i][1]
5143 + curve[2][d] * tt[i][2]
5144 + curve[3][d] * tt[i][3];
5145 }
5146 }
5147
5148 for(d=0; d<ndots; d++) {
5149 #ifdef DEBUG_DOTCURVE
5150 printf("dot %d ", d); printdot(dots[d].p); printf(":\n");
5151
5152 /* for debugging */
5153 for(i=0; i< NAPSECT; i++) {
5154 dist1 = fdotsegdist2(&cvd[i], dots[d].p);
5155 printf(" seg %d ",i); printseg(&cvd[i]); printf(" dist=%g\n", sqrt(dist1));
5156 }
5157 #endif
5158
5159 x = dots[d].p[X];
5160 y = dots[d].p[Y];
5161
5162 /* find the nearest dot on the curve
5163 * there may be up to 2 local minimums - so we start from the
5164 * ends of curve and go to the center
5165 */
5166
5167 id1 = 0;
5168 dx = x - cvd[0][X];
5169 dy = y - cvd[0][Y];
5170 dist1 = dx*dx + dy*dy;
5171 #ifdef DEBUG_DOTCURVE
5172 printf(" dot 0 "); printdot(cvd[id1]); printf(" dist=%g\n", sqrt(dist1));
5173 #endif
5174 for(i = 1; i<=NAPSECT; i++) {
5175 dx = x - cvd[i][X];
5176 dy = y - cvd[i][Y];
5177 dist3 = dx*dx + dy*dy;
5178 #ifdef DEBUG_DOTCURVE
5179 printf(" dot %d ",i); printdot(cvd[i]); printf(" dist=%g\n", sqrt(dist3));
5180 #endif
5181 if(dist3 < dist1) {
5182 dist1 = dist3;
5183 id1 = i;
5184 } else
5185 break;
5186 }
5187
5188 if(id1 < NAPSECT-1) {
5189 id2 = NAPSECT;
5190 dx = x - cvd[NAPSECT][X];
5191 dy = y - cvd[NAPSECT][Y];
5192 dist2 = dx*dx + dy*dy;
5193 #ifdef DEBUG_DOTCURVE
5194 printf(" +dot %d ", id2); printdot(cvd[id2]); printf(" dist=%g\n", sqrt(dist2));
5195 #endif
5196 for(i = NAPSECT-1; i>id1+1; i--) {
5197 dx = x - cvd[i][X];
5198 dy = y - cvd[i][Y];
5199 dist3 = dx*dx + dy*dy;
5200 #ifdef DEBUG_DOTCURVE
5201 printf(" dot %d ",i); printdot(cvd[i]); printf(" dist=%g\n", sqrt(dist3));
5202 #endif
5203 if(dist3 < dist2) {
5204 dist2 = dist3;
5205 id2 = i;
5206 } else
5207 break;
5208 }
5209
5210 /* now find which of the local minimums is smaller */
5211 if(dist2 < dist1) {
5212 id1 = id2;
5213 }
5214 }
5215
5216 /* the nearest segment must include the nearest dot */
5217 if(id1==0) {
5218 dots[d].seg = 0;
5219 dots[d].dist2 = fdotsegdist2(&cvd[0], dots[d].p);
5220 } else if(id1==NAPSECT) {
5221 dots[d].seg = NAPSECT-1;
5222 dots[d].dist2 = fdotsegdist2(&cvd[NAPSECT-1], dots[d].p);
5223 } else {
5224 dist1 = fdotsegdist2(&cvd[id1], dots[d].p);
5225 dist2 = fdotsegdist2(&cvd[id1-1], dots[d].p);
5226 if(dist2 < dist1) {
5227 dots[d].seg = id1-1;
5228 dots[d].dist2 = dist2;
5229 } else {
5230 dots[d].seg = id1;
5231 dots[d].dist2 = dist1;
5232 }
5233 }
5234
5235 i = dots[d].seg % NWSECT;
5236 sum[i] += dots[d].dist2;
5237 if(dots[d].dist2 > max)
5238 max = dots[d].dist2;
5239 count[i]++;
5240 #ifdef DEBUG_DOTCURVE
5241 printf(" best seg %d sect %d dist=%g\n", dots[d].seg, i, sqrt(dots[d].dist2));
5242 #endif
5243 }
5244
5245 /* calculate the weighted average */
5246 id1=0;
5247 dist1=0.;
5248 for(i=0; i<NWSECT; i++) {
5249 if(count[i]==0)
5250 continue;
5251 id1++;
5252 dist1 += sum[i]/count[i];
5253 }
5254 if(maxp)
5255 *maxp = max;
5256 if(id1==0) /* no dots, strange */
5257 return 0.;
5258 else
5259 return dist1/id1; /* to get the average distance apply sqrt() */
5260 }
5261
5262 /*
5263 * Approximate a curve matching the giving set of points and with
5264 * middle reference points going along the given segments (and no farther
5265 * than these segments).
5266 */
5267
5268 void
fapproxcurve(double cv[4][2],struct dot_dist * dots,int ndots)5269 fapproxcurve(
5270 double cv[4][2 /*X,Y*/ ], /* points 0-3 are passed in, points 1,2 - out */
5271 struct dot_dist *dots, /* the dots to approximate - distances returned
5272 * there may be invalid */
5273 int ndots
5274 )
5275 {
5276 /* b and c are the middle control points */
5277 #define B 0
5278 #define C 1
5279 /* maximal number of sections on each axis - used for the first step */
5280 #define MAXSECT 2
5281 /* number of sections used for the other steps */
5282 #define NORMSECT 2
5283 /* when the steps become less than this many points, it's time to stop */
5284 #define STEPEPS 1.
5285 double from[2 /*B,C*/], to[2 /*B,C*/];
5286 double middf[2 /*B,C*/][2 /*X,Y*/], df;
5287 double coef[2 /*B,C*/][MAXSECT];
5288 double res[MAXSECT][MAXSECT], thisres, bestres, goodres;
5289 int ncoef[2 /*B,C*/], best[2 /*B,C*/], good[2 /*B,C*/];
5290 int i, j, k, keepsym;
5291 char bc[]="BC";
5292 char xy[]="XY";
5293
5294 #ifdef DEBUG_APPROXCV
5295 fprintf(stderr, "Curve points:");
5296 for(i=0; i<4; i++) {
5297 fprintf(stderr, " ");
5298 printdot(cv[i]);
5299 }
5300 fprintf(stderr, "\nDots:");
5301 for(i=0; i<ndots; i++) {
5302 fprintf(stderr, " ");
5303 printdot(dots[i].p);
5304 }
5305 fprintf(stderr, "\n");
5306 #endif
5307
5308 /* load the endpoints and calculate differences */
5309 for(i=0; i<2; i++) {
5310 /* i is X, Y */
5311 middf[B][i] = cv[1][i]-cv[0][i];
5312 middf[C][i] = cv[2][i]-cv[3][i];
5313
5314 /* i is B, C */
5315 from[i] = 0.;
5316 to[i] = 1.;
5317 ncoef[i] = MAXSECT;
5318 }
5319
5320 while(ncoef[B] != 1 || ncoef[C] != 1) {
5321 /* prepare the values of coefficients */
5322 for(i=0; i<2; i++) { /*B,C*/
5323 #ifdef DEBUG_APPROXCV
5324 fprintf(stderr, "Coefficients by %c(%g,%g):", bc[i], from[i], to[i]);
5325 #endif
5326 df = (to[i]-from[i]) / (ncoef[i]*2);
5327 for(j=0; j<ncoef[i]; j++) {
5328 coef[i][j] = from[i] + df*(2*j+1);
5329 #ifdef DEBUG_APPROXCV
5330 fprintf(stderr, " %g", coef[i][j]);
5331 #endif
5332 }
5333 #ifdef DEBUG_APPROXCV
5334 fprintf(stderr, "\n");
5335 #endif
5336 }
5337 bestres = FBIGVAL;
5338 best[B] = best[C] = 0;
5339 /* i iterates by ncoef[B], j iterates by ncoef[C] */
5340 for(i=0; i<ncoef[B]; i++) {
5341 for(j=0; j<ncoef[C]; j++) {
5342 for(k=0; k<2; k++) { /*X, Y*/
5343 cv[1][k] = cv[0][k] + middf[B][k]*coef[B][i];
5344 cv[2][k] = cv[3][k] + middf[C][k]*coef[C][j];
5345 }
5346 res[i][j] = thisres = fdotcurvdist2(cv, dots, ndots, NULL);
5347 if(thisres < bestres) {
5348 goodres = bestres;
5349 good[B] = best[B];
5350 good[C] = best[C];
5351 bestres = thisres;
5352 best[B] = i;
5353 best[C] = j;
5354 } else if(thisres < goodres) {
5355 goodres = thisres;
5356 good[B] = i;
5357 good[C] = j;
5358 }
5359 #ifdef DEBUG_APPROXCV
5360 fprintf(stderr, " at (%g,%g) dist=%g %s\n", coef[B][i], coef[C][j], sqrt(thisres),
5361 (best[B]==i && best[C]==j)? "(BEST)":"");
5362 #endif
5363 }
5364 }
5365 #ifdef DEBUG_APPROXCV
5366 fprintf(stderr, " best: at (%g, %g) dist=%g\n",
5367 coef[B][best[B]], coef[C][best[C]], sqrt(bestres));
5368 fprintf(stderr, " B:%d,%d C:%d,%d -- 2nd best: at (%g, %g) dist=%g\n",
5369 best[B], good[B], best[C], good[C], coef[B][good[B]], coef[C][good[C]], sqrt(goodres));
5370 #endif
5371
5372 if(bestres < (0.1*0.1)) { /* consider it close enough */
5373 /* calculate the coordinates to return */
5374 for(k=0; k<2; k++) { /*X, Y*/
5375 cv[1][k] = cv[0][k] + middf[B][k]*coef[B][best[B]];
5376 cv[2][k] = cv[3][k] + middf[C][k]*coef[C][best[C]];
5377 }
5378 #ifdef DEBUG_APPROXCV
5379 fprintf(stderr, "quick approximated middle points "); printdot(cv[1]);
5380 fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n");
5381 #endif
5382 return;
5383 }
5384 keepsym = 0;
5385 if(best[B] != best[C] && best[B]-best[C] == good[C]-good[B]) {
5386 keepsym = 1;
5387 #ifdef DEBUG_APPROXCV
5388 fprintf(stderr, "keeping symmetry!\n");
5389 #endif
5390 }
5391 for(i=0; i<2; i++) { /*B,C*/
5392 if(ncoef[i]==1)
5393 continue;
5394 if(keepsym) {
5395 /* try to keep the symmetry */
5396 if(best[i] < good[i]) {
5397 from[i] = coef[i][best[i]];
5398 to[i] = coef[i][good[i]];
5399 } else {
5400 from[i] = coef[i][good[i]];
5401 to[i] = coef[i][best[i]];
5402 }
5403 } else {
5404 df = (to[i]-from[i]) / ncoef[i];
5405 from[i] += df*best[i];
5406 to[i] = from[i] + df;
5407 }
5408 if( fabs(df*middf[i][0]) < STEPEPS && fabs(df*middf[i][1]) < STEPEPS) {
5409 /* this side has converged */
5410 from[i] = to[i] = (from[i]+to[i]) / 2.;
5411 ncoef[i] = 1;
5412 } else
5413 ncoef[i] = NORMSECT;
5414 }
5415
5416 }
5417 /* calculate the coordinates to return */
5418 for(k=0; k<2; k++) { /*X, Y*/
5419 cv[1][k] = cv[0][k] + middf[B][k]*from[B];
5420 cv[2][k] = cv[3][k] + middf[C][k]*from[C];
5421 }
5422 #ifdef DEBUG_APPROXCV
5423 fprintf(stderr, "approximated middle points "); printdot(cv[1]);
5424 fprintf(stderr, " "); printdot(cv[2]); fprintf(stderr, "\n");
5425 #endif
5426 #undef B
5427 #undef C
5428 #undef MAXSECT
5429 #undef NORMSECT
5430 #undef STEPEPS
5431 }
5432
5433 /*
5434 * Find the squared value of the sinus of the angle between the
5435 * end of ge1 and the beginning of ge2
5436 * The curve must be normalized.
5437 */
5438
5439 static double
fjointsin2(GENTRY * ge1,GENTRY * ge2)5440 fjointsin2(
5441 GENTRY *ge1,
5442 GENTRY *ge2
5443 )
5444 {
5445 double d[3][2 /*X,Y*/];
5446 double scale1, scale2, len1, len2;
5447 int axis;
5448
5449 if(ge1->rtg < 0) {
5450 d[1][X] = ge1->fx3 - ge1->prev->fx3;
5451 d[1][Y] = ge1->fy3 - ge1->prev->fy3;
5452 } else {
5453 d[1][X] = ge1->fx3 - ge1->fpoints[X][ge1->rtg];
5454 d[1][Y] = ge1->fy3 - ge1->fpoints[Y][ge1->rtg];
5455 }
5456 d[2][X] = ge2->fpoints[X][ge2->ftg] - ge2->prev->fx3;
5457 d[2][Y] = ge2->fpoints[Y][ge2->ftg] - ge2->prev->fy3;
5458
5459 len1 = sqrt( d[1][X]*d[1][X] + d[1][Y]*d[1][Y] );
5460 len2 = sqrt( d[2][X]*d[2][X] + d[2][Y]*d[2][Y] );
5461 /* scale the 2nd segment to the length of 1
5462 * and to make sure that the 1st segment is longer scale it to
5463 * the length of 2 and extend to the same distance backwards
5464 */
5465 scale1 = 2./len1;
5466 scale2 = 1./len2;
5467
5468 for(axis=0; axis <2; axis++) {
5469 d[0][axis] = -( d[1][axis] *= scale1 );
5470 d[2][axis] *= scale2;
5471 }
5472 return fdotsegdist2(d, d[2]);
5473 }
5474
5475 #if 0
5476 /* find the area covered by the curve
5477 * (limited by the projections to the X axis)
5478 */
5479
5480 static double
5481 fcvarea(
5482 GENTRY *ge
5483 )
5484 {
5485 double Ly, My, Ny, Py, Qx, Rx, Sx;
5486 double area;
5487
5488 /* y = Ly*t^3 + My*t^2 + Ny*t + Py */
5489 Ly = -ge->prev->fy3 + 3*(ge->fy1 - ge->fy2) + ge->fy3;
5490 My = 3*ge->prev->fy3 - 6*ge->fy1 + 3*ge->fy2;
5491 Ny = 3*(-ge->prev->fy3 + ge->fy1);
5492 Py = ge->prev->fy3;
5493
5494 /* dx/dt = Qx*t^2 + Rx*t + Sx */
5495 Qx = 3*(-ge->prev->fx3 + 3*(ge->fx1 - ge->fx2) + ge->fx3);
5496 Rx = 6*(ge->prev->fx3 - 2*ge->fx1 + ge->fx2);
5497 Sx = 3*(-ge->prev->fx3 + ge->fx1);
5498
5499 /* area is integral[from 0 to 1]( y(t) * dx(t)/dt *dt) */
5500 area = 1./6.*(Ly*Qx) + 1./5.*(Ly*Rx + My*Qx)
5501 + 1./4.*(Ly*Sx + My*Rx + Ny*Qx) + 1./3.*(My*Sx + Ny*Rx + Py*Qx)
5502 + 1./2.*(Ny*Sx + Py*Rx) + Py*Sx;
5503
5504 return area;
5505 }
5506 #endif
5507
5508 /* find the value of point on the curve at the given parameter t,
5509 * along the given axis (0 - X, 1 - Y).
5510 */
5511
5512 static double
fcvval(GENTRY * ge,int axis,double t)5513 fcvval(
5514 GENTRY *ge,
5515 int axis,
5516 double t
5517 )
5518 {
5519 double t2, mt, mt2;
5520
5521 /* val = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 */
5522 t2 = t*t;
5523 mt = 1-t;
5524 mt2 = mt*mt;
5525
5526 return ge->prev->fpoints[axis][2]*mt2*mt
5527 + 3*(ge->fpoints[axis][0]*mt2*t + ge->fpoints[axis][1]*mt*t2)
5528 + ge->fpoints[axis][2]*t*t2;
5529 }
5530
5531 /*
5532 * Find ndots equally spaced dots on a curve or line and fill
5533 * their coordinates into the dots array
5534 */
5535
5536 static void
fsampledots(GENTRY * ge,double dots[][2],int ndots)5537 fsampledots(
5538 GENTRY *ge,
5539 double dots[][2], /* the dots to fill */
5540 int ndots
5541 )
5542 {
5543 int i, axis;
5544 double t, nf, dx, d[2];
5545
5546 nf = ndots+1;
5547 if(ge->type == GE_CURVE) {
5548 for(i=0; i<ndots; i++) {
5549 t = (i+1)/nf;
5550 for(axis=0; axis<2; axis++)
5551 dots[i][axis] = fcvval(ge, axis, t);
5552 }
5553 } else { /* line */
5554 d[0] = ge->fx3 - ge->prev->fx3;
5555 d[1] = ge->fy3 - ge->prev->fy3;
5556 for(i=0; i<ndots; i++) {
5557 t = (i+1)/nf;
5558 for(axis=0; axis<2; axis++)
5559 dots[i][axis] = ge->prev->fpoints[axis][2]
5560 + t*d[axis];
5561 }
5562 }
5563 }
5564
5565 /*
5566 * Allocate a structure gex_con
5567 */
5568
5569 static void
alloc_gex_con(GENTRY * ge)5570 alloc_gex_con(
5571 GENTRY *ge
5572 )
5573 {
5574 ge->ext = (void*)calloc(1, sizeof(GEX_CON));
5575 if(ge->ext == 0) {
5576 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
5577 exit(255);
5578 }
5579 }
5580
5581 /*
5582 * Normalize a gentry for fforceconcise() : find the points that
5583 * can be used to calculate the tangents.
5584 */
5585
5586 static void
fnormalizege(GENTRY * ge)5587 fnormalizege(
5588 GENTRY *ge
5589 )
5590 {
5591 int midsame, frontsame, rearsame;
5592
5593 if(ge->type == GE_LINE) {
5594 ge->ftg = 2;
5595 ge->rtg = -1;
5596 } else { /* assume it's a curve */
5597 midsame = (fabs(ge->fx1-ge->fx2)<FEPS && fabs(ge->fy1-ge->fy2)<FEPS);
5598 frontsame = (fabs(ge->fx1-ge->prev->fx3)<FEPS && fabs(ge->fy1-ge->prev->fy3)<FEPS);
5599 rearsame = (fabs(ge->fx3-ge->fx2)<FEPS && fabs(ge->fy3-ge->fy2)<FEPS);
5600
5601 if(midsame && (frontsame || rearsame) ) {
5602 /* essentially a line */
5603 ge->ftg = 2;
5604 ge->rtg = -1;
5605 } else {
5606 if(frontsame) {
5607 ge->ftg = 1;
5608 } else {
5609 ge->ftg = 0;
5610 }
5611 if(rearsame) {
5612 ge->rtg = 0;
5613 } else {
5614 ge->rtg = 1;
5615 }
5616 }
5617 }
5618 }
5619
5620 /* various definition for the processing of outlines */
5621
5622 /* maximal average quadratic distance from the original curve
5623 * (in dots) to consider the joined curve good
5624 */
5625 #define CVEPS 1.5
5626 #define CVEPS2 (CVEPS*CVEPS)
5627 /* squared sinus of the maximal angle that we consider a smooth joint */
5628 #define SMOOTHSIN2 0.25 /* 0.25==sin(30 degrees)^2 */
5629 /* squared line length that we consider small */
5630 #define SMALL_LINE2 (15.*15.)
5631 /* how many times a curve must be bigger than a line to join, squared */
5632 #define TIMES_LINE2 (3.*3.)
5633
5634 /*
5635 * Normalize and analyse a gentry for fforceconcise() and fill out the gex_con
5636 * structure
5637 */
5638
5639 static void
fanalyzege(GENTRY * ge)5640 fanalyzege(
5641 GENTRY *ge
5642 )
5643 {
5644 int i, ix, iy;
5645 double avsd2, dots[3][2 /*X,Y*/];
5646 GEX_CON *gex;
5647
5648 gex = X_CON(ge);
5649 memset(gex, 0, sizeof *gex);
5650
5651 gex->len2 = 0;
5652 for(i=0; i<2; i++) {
5653 avsd2 = gex->d[i] = ge->fpoints[i][2] - ge->prev->fpoints[i][2];
5654 gex->len2 += avsd2*avsd2;
5655 }
5656 gex->sin2 = fjointsin2(ge, ge->frwd);
5657 if(ge->type == GE_CURVE) {
5658 ge->dir = fgetcvdir(ge);
5659 for(i=0; i<2; i++) {
5660 dots[0][i] = ge->prev->fpoints[i][2];
5661 dots[1][i] = ge->fpoints[i][2];
5662 dots[2][i] = fcvval(ge, i, 0.5);
5663 }
5664 avsd2 = fdotsegdist2(dots, dots[2]);
5665 if(avsd2 <= CVEPS2) {
5666 gex->flags |= GEXF_FLAT;
5667 }
5668 } else {
5669 ge->dir = CVDIR_FEQUAL|CVDIR_REQUAL;
5670 gex->flags |= GEXF_FLAT;
5671 }
5672 if(gex->flags & GEXF_FLAT) {
5673 if( fabs(gex->d[X]) > FEPS && fabs(gex->d[Y]) < 5.
5674 && fabs(gex->d[Y] / gex->d[X]) < 0.2)
5675 gex->flags |= GEXF_HOR;
5676 else if( fabs(gex->d[Y]) > FEPS && fabs(gex->d[X]) < 5.
5677 && fabs(gex->d[X] / gex->d[Y]) < 0.2)
5678 gex->flags |= GEXF_VERT;
5679 }
5680 ix = gex->isd[X] = fsign(gex->d[X]);
5681 iy = gex->isd[Y] = fsign(gex->d[Y]);
5682 if(ix <= 0) {
5683 if(iy <= 0)
5684 gex->flags |= GEXF_QDL;
5685 if(iy >= 0)
5686 gex->flags |= GEXF_QUL;
5687 if(gex->flags & GEXF_HOR)
5688 gex->flags |= GEXF_IDQ_L;
5689 }
5690 if(ix >= 0) {
5691 if(iy <= 0)
5692 gex->flags |= GEXF_QDR;
5693 if(iy >= 0)
5694 gex->flags |= GEXF_QUR;
5695 if(gex->flags & GEXF_HOR)
5696 gex->flags |= GEXF_IDQ_R;
5697 }
5698 if(gex->flags & GEXF_VERT) {
5699 if(iy <= 0) {
5700 gex->flags |= GEXF_IDQ_U;
5701 } else { /* supposedly there is no 0-sized entry */
5702 gex->flags |= GEXF_IDQ_D;
5703 }
5704 }
5705 }
5706
5707 /*
5708 * Analyse a joint between this and following gentry for fforceconcise()
5709 * and fill out the corresponding parts of the gex_con structure
5710 * Bothe entries must be analyzed first.
5711 */
5712
5713 static void
fanalyzejoint(GENTRY * ge)5714 fanalyzejoint(
5715 GENTRY *ge
5716 )
5717 {
5718 GENTRY *nge = ge->frwd;
5719 GENTRY tge;
5720 GEX_CON *gex, *ngex;
5721 double avsd2, dots[3][2 /*X,Y*/];
5722 int i;
5723
5724 gex = X_CON(ge); ngex = X_CON(nge);
5725
5726 /* look if they can be joined honestly */
5727
5728 /* if any is flat, they should join smoothly */
5729 if( (gex->flags & GEXF_FLAT || ngex->flags & GEXF_FLAT)
5730 && gex->sin2 > SMOOTHSIN2)
5731 goto try_flatboth;
5732
5733 if(ge->type == GE_LINE) {
5734 if(nge->type == GE_LINE) {
5735 if(gex->len2 > SMALL_LINE2 || ngex->len2 > SMALL_LINE2)
5736 goto try_flatboth;
5737 } else {
5738 if(gex->len2*TIMES_LINE2 > ngex->len2)
5739 goto try_flatboth;
5740 }
5741 } else if(nge->type == GE_LINE) {
5742 if(ngex->len2*TIMES_LINE2 > gex->len2)
5743 goto try_flatboth;
5744 }
5745
5746 /* if curve changes direction */
5747 if( gex->isd[X]*ngex->isd[X]<0 || gex->isd[Y]*ngex->isd[Y]<0)
5748 goto try_idealone;
5749
5750 /* if would create a zigzag */
5751 if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 )
5752 goto try_flatone;
5753
5754 if( fcrossraysge(ge, nge, NULL, NULL, NULL) )
5755 gex->flags |= GEXF_JGOOD;
5756
5757 try_flatone:
5758 /* look if they can be joined by flatting out one of the entries */
5759
5760 /* at this point we know that the general direction of the
5761 * gentries is OK
5762 */
5763
5764 if( gex->flags & GEXF_FLAT ) {
5765 tge = *ge;
5766 tge.fx1 = tge.fx3;
5767 tge.fy1 = tge.fy3;
5768 fnormalizege(&tge);
5769 if( fcrossraysge(&tge, nge, NULL, NULL, NULL) )
5770 gex->flags |= GEXF_JFLAT|GEXF_JFLAT1;
5771 }
5772 if( ngex->flags & GEXF_FLAT ) {
5773 tge = *nge;
5774 tge.fx2 = ge->fx3;
5775 tge.fy2 = ge->fy3;
5776 fnormalizege(&tge);
5777 if( fcrossraysge(ge, &tge, NULL, NULL, NULL) )
5778 gex->flags |= GEXF_JFLAT|GEXF_JFLAT2;
5779 }
5780
5781 try_idealone:
5782 /* look if one of the entries can be brought to an idealized
5783 * horizontal or vertical position and then joined
5784 */
5785 if( gex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) {
5786 tge = *ge;
5787 tge.fx1 = tge.fx3;
5788 tge.fy1 = ge->prev->fy3; /* force horizontal */
5789 fnormalizege(&tge);
5790 if( fcrossraysge(&tge, nge, NULL, NULL, NULL) )
5791 gex->flags |= GEXF_JID|GEXF_JID1;
5792 } else if( gex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) {
5793 tge = *ge;
5794 tge.fx1 = ge->prev->fx3; /* force vertical */
5795 tge.fy1 = tge.fy3;
5796 fnormalizege(&tge);
5797 if( fcrossraysge(&tge, nge, NULL, NULL, NULL) )
5798 gex->flags |= GEXF_JID|GEXF_JID1;
5799 }
5800 if( ngex->flags & GEXF_HOR && gex->isd[X]*ngex->isd[X]>=0 ) {
5801 tge = *nge;
5802 tge.fx2 = ge->fx3;
5803 tge.fy2 = nge->fy3; /* force horizontal */
5804 fnormalizege(&tge);
5805 if( fcrossraysge(ge, &tge, NULL, NULL, NULL) )
5806 gex->flags |= GEXF_JID|GEXF_JID2;
5807 } else if( ngex->flags & GEXF_VERT && gex->isd[Y]*ngex->isd[Y]>=0 ) {
5808 tge = *nge;
5809 tge.fx2 = nge->fx3; /* force vertical */
5810 tge.fy2 = ge->fy3;
5811 fnormalizege(&tge);
5812 if( fcrossraysge(ge, &tge, NULL, NULL, NULL) )
5813 gex->flags |= GEXF_JID|GEXF_JID2;
5814 }
5815
5816 try_flatboth:
5817 /* look if we can change them to one line */
5818 if(gex->flags & GEXF_FLAT && ngex->flags & GEXF_FLAT) {
5819 for(i=0; i<2; i++) {
5820 dots[0][i] = ge->prev->fpoints[i][2];
5821 dots[1][i] = nge->fpoints[i][2];
5822 dots[2][i] = ge->fpoints[i][2];
5823 }
5824 if( fdotsegdist2(dots, dots[2]) <= CVEPS2)
5825 gex->flags |= GEXF_JLINE;
5826 }
5827 }
5828
5829 /*
5830 * Force conciseness of one contour in the glyph,
5831 * the contour is indicated by one entry from it.
5832 */
5833
5834 static void
fconcisecontour(GLYPH * g,GENTRY * startge)5835 fconcisecontour(
5836 GLYPH *g,
5837 GENTRY *startge
5838 )
5839 {
5840 /* initial maximal number of dots to be used as reference */
5841 #define MAXDOTS ((NREFDOTS+1)*12)
5842
5843 GENTRY *ge, *pge, *nge, *ige;
5844 GEX_CON *gex, *pgex, *ngex, *nngex;
5845 GENTRY tpge, tnge;
5846 int quad, qq, i, j, ndots, maxdots;
5847 int found[2];
5848 int joinmask, pflag, nflag;
5849 struct dot_dist *dots;
5850 double avsd2, maxd2, eps2;
5851 double apcv[4][2];
5852
5853 if(startge == 0) {
5854 fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n",
5855 __FILE__, __LINE__);
5856 fprintf(stderr, "Strange contour in glyph %s\n", g->name);
5857 dumppaths(g, NULL, NULL);
5858 return;
5859 }
5860
5861 if(startge->type != GE_CURVE && startge->type != GE_LINE)
5862 return; /* probably a degenerate contour */
5863
5864 if(ISDBG(FCONCISE))
5865 fprintf(stderr, "processing contour 0x%p of glyph %s\n", startge, g->name);
5866
5867 maxdots = MAXDOTS;
5868 dots = (struct dot_dist *)malloc(sizeof(*dots)*maxdots);
5869 if(dots == NULL) {
5870 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
5871 exit(255);
5872 }
5873
5874 ge = startge;
5875 joinmask = GEXF_JGOOD;
5876 while(1) {
5877 restart:
5878 gex = X_CON(ge);
5879 if((gex->flags & GEXF_JMASK) > ((joinmask<<1)-1)) {
5880 if(ISDBG(FCONCISE))
5881 fprintf(stderr, "found higher flag (%x>%x) at 0x%p\n",
5882 gex->flags & GEXF_JMASK, ((joinmask<<1)-1), ge);
5883 joinmask <<= 1;
5884 startge = ge; /* have to redo the pass */
5885 continue;
5886 }
5887 if(( gex->flags & joinmask )==0)
5888 goto next;
5889
5890 /* if we happen to be in the middle of a string of
5891 * joinable entries, find its beginning
5892 */
5893 if( gex->flags & (GEXF_JCVMASK^GEXF_JID) )
5894 quad = gex->flags & X_CON_F(ge->frwd) & GEXF_QMASK;
5895 else if( gex->flags & GEXF_JID2 )
5896 quad = gex->flags & GEXF_QFROM_IDEAL(X_CON_F(ge->frwd)) & GEXF_QMASK;
5897 else /* must be GEXF_JID1 */
5898 quad = GEXF_QFROM_IDEAL(gex->flags) & X_CON_F(ge->frwd) & GEXF_QMASK;
5899
5900 pge = ge;
5901 pgex = X_CON(pge->bkwd);
5902
5903 if(ISDBG(FCONCISE))
5904 fprintf(stderr, "ge %p prev -> 0x%p ", ge, pge);
5905
5906 while(pgex->flags & GEXF_JCVMASK) {
5907 if( !(pgex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID2)) )
5908 qq = GEXF_QFROM_IDEAL(pgex->flags);
5909 else
5910 qq = pgex->flags & GEXF_QMASK;
5911
5912 if(ISDBG(FCONCISE))
5913 fprintf(stderr, "(%x?%x)", quad, qq);
5914
5915 if( !(quad & qq) ) {
5916 if( !(X_CON_F(pge) & (GEXF_JCVMASK^GEXF_JID))
5917 && pgex->flags & (GEXF_JCVMASK^GEXF_JID) ) {
5918 /* the previos entry is definitely a better match */
5919 if(pge == ge) {
5920 if(ISDBG(FCONCISE))
5921 fprintf(stderr, "\nprev is a better match at %p\n", pge);
5922 startge = ge;
5923 goto next;
5924 } else
5925 pge = pge->frwd;
5926 }
5927 break;
5928 }
5929
5930 quad &= qq;
5931 pge = pge->bkwd;
5932 pgex = X_CON(pge->bkwd);
5933 if(ISDBG(FCONCISE))
5934 fprintf(stderr, "0x%p ", pge);
5935 }
5936
5937 /* collect as many entries for joining as possible */
5938 nge = ge->frwd;
5939 ngex = X_CON(nge);
5940 nngex = X_CON(nge->frwd);
5941
5942 if(ISDBG(FCONCISE))
5943 fprintf(stderr, ": 0x%x\nnext -> 0x%p ", pge, nge);
5944
5945 while(ngex->flags & GEXF_JCVMASK) {
5946 if( !(ngex->flags & ((GEXF_JCVMASK^GEXF_JID)|GEXF_JID1)) )
5947 qq = GEXF_QFROM_IDEAL(nngex->flags);
5948 else
5949 qq = nngex->flags & GEXF_QMASK;
5950
5951 if(ISDBG(FCONCISE))
5952 fprintf(stderr, "(%x?%x)", quad, qq);
5953 if( !(quad & qq) ) {
5954 if( !(X_CON_F(nge->bkwd) & (GEXF_JCVMASK^GEXF_JID))
5955 && ngex->flags & (GEXF_JCVMASK^GEXF_JID) ) {
5956 /* the next-next entry is definitely a better match */
5957 if(nge == ge->frwd) {
5958 if(ISDBG(FCONCISE))
5959 fprintf(stderr, "\nnext %x is a better match than %x at %p (jmask %x)\n",
5960 ngex->flags & GEXF_JCVMASK, gex->flags & GEXF_JCVMASK, nge, joinmask);
5961 goto next;
5962 } else
5963 nge = nge->bkwd;
5964 }
5965 break;
5966 }
5967
5968 quad &= qq;
5969 nge = nge->frwd;
5970 ngex = nngex;
5971 nngex = X_CON(nge->frwd);
5972 if(ISDBG(FCONCISE))
5973 fprintf(stderr, "0x%p ", nge);
5974 }
5975
5976 if(ISDBG(FCONCISE))
5977 fprintf(stderr, ": 0x%x\n", nge);
5978
5979 /* XXX add splitting of last entries if neccessary */
5980
5981 /* make sure that all the reference dots are valid */
5982 for(ige = pge; ige != nge->frwd; ige = ige->frwd) {
5983 nngex = X_CON(ige);
5984 if( !(nngex->flags & GEXF_VDOTS) ) {
5985 fsampledots(ige, nngex->dots, NREFDOTS);
5986 nngex->flags |= GEXF_VDOTS;
5987 }
5988 }
5989
5990 /* do the actual joining */
5991 while(1) {
5992 pgex = X_CON(pge);
5993 ngex = X_CON(nge->bkwd);
5994 /* now the segments to be joined are pge...nge */
5995
5996 ndots = 0;
5997 for(ige = pge; ige != nge->frwd; ige = ige->frwd) {
5998 if(maxdots < ndots+(NREFDOTS+1)) {
5999 maxdots += MAXDOTS;
6000 dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots);
6001 if(dots == NULL) {
6002 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
6003 exit(255);
6004 }
6005 }
6006 nngex = X_CON(ige);
6007 for(i=0; i<NREFDOTS; i++) {
6008 for(j=0; j<2; j++)
6009 dots[ndots].p[j] = nngex->dots[i][j];
6010 ndots++;
6011 }
6012 for(j=0; j<2; j++)
6013 dots[ndots].p[j] = ige->fpoints[j][2];
6014 ndots++;
6015 }
6016 ndots--; /* the last point is not interesting */
6017
6018 tpge = *pge;
6019 pflag = pgex->flags;
6020 if(pflag & (GEXF_JGOOD|GEXF_JFLAT2|GEXF_JID2)) {
6021 /* nothing */
6022 } else if(pflag & GEXF_JFLAT) {
6023 tpge.fx1 = tpge.fx3;
6024 tpge.fy1 = tpge.fy3;
6025 } else if(pflag & GEXF_JID) {
6026 if(pflag & GEXF_HOR)
6027 tpge.fy1 = tpge.bkwd->fy3;
6028 else
6029 tpge.fx1 = tpge.bkwd->fx3;
6030 }
6031
6032 tnge = *nge;
6033 nflag = ngex->flags;
6034 if(nflag & (GEXF_JGOOD|GEXF_JFLAT1|GEXF_JID)
6035 && !(nflag & GEXF_JID2)) {
6036 /* nothing */
6037 } else if(nflag & GEXF_JFLAT) {
6038 tnge.fx2 = tnge.bkwd->fx3;
6039 tnge.fy2 = tnge.bkwd->fy3;
6040 } else if(nflag & GEXF_JID) {
6041 if(X_CON_F(nge) & GEXF_HOR)
6042 tnge.fy2 = tnge.fy3;
6043 else
6044 tnge.fx2 = tnge.fx3;
6045 }
6046
6047 fnormalizege(&tpge);
6048 fnormalizege(&tnge);
6049 if( fcrossraysge(&tpge, &tnge, NULL, NULL, &apcv[1]) ) {
6050 apcv[0][X] = tpge.bkwd->fx3;
6051 apcv[0][Y] = tpge.bkwd->fy3;
6052 /* apcv[1] and apcv[2] were filled by fcrossraysge() */
6053 apcv[3][X] = tnge.fx3;
6054 apcv[3][Y] = tnge.fy3;
6055
6056 /* calculate the precision depending on the smaller dimension of the curve */
6057 maxd2 = apcv[3][X]-apcv[0][X];
6058 maxd2 *= maxd2;
6059 eps2 = apcv[3][Y]-apcv[0][Y];
6060 eps2 *= eps2;
6061 if(maxd2 < eps2)
6062 eps2 = maxd2;
6063 eps2 *= (CVEPS2*4.) / (400.*400.);
6064 if(eps2 < CVEPS2)
6065 eps2 = CVEPS2;
6066 else if(eps2 > CVEPS2*4.)
6067 eps2 = CVEPS2*4.;
6068
6069 fapproxcurve(apcv, dots, ndots);
6070
6071 avsd2 = fdotcurvdist2(apcv, dots, ndots, &maxd2);
6072 if(ISDBG(FCONCISE))
6073 fprintf(stderr, "avsd = %g, maxd = %g, ", sqrt(avsd2), sqrt(maxd2));
6074 if(avsd2 <= eps2 && maxd2 <= eps2*2.) {
6075 /* we've guessed a curve that is close enough */
6076 ggoodcv++; ggoodcvdots += ndots;
6077
6078 if(ISDBG(FCONCISE)) {
6079 fprintf(stderr, "in %s joined %p-%p to ", g->name, pge, nge);
6080 for(i=0; i<4; i++) {
6081 fprintf(stderr, " (%g, %g)", apcv[i][X], apcv[i][Y]);
6082 }
6083 fprintf(stderr, " from\n");
6084 dumppaths(g, pge, nge);
6085 }
6086 for(i=0; i<3; i++) {
6087 pge->fxn[i] = apcv[i+1][X];
6088 pge->fyn[i] = apcv[i+1][Y];
6089 }
6090 pge->type = GE_CURVE;
6091 ge = pge;
6092 for(ige = pge->frwd; ; ige = pge->frwd) {
6093 if(ige == pge) {
6094 fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n",
6095 __FILE__, __LINE__);
6096 free(dots);
6097 return;
6098 }
6099 if(startge == ige)
6100 startge = pge;
6101 free(ige->ext);
6102 freethisge(ige);
6103 if(ige == nge)
6104 break;
6105 }
6106 fnormalizege(ge);
6107 if(ISDBG(FCONCISE)) {
6108 fprintf(stderr, "normalized ");
6109 for(i=0; i<3; i++) {
6110 fprintf(stderr, " (%g, %g)", ge->fpoints[X][i], ge->fpoints[Y][i]);
6111 }
6112 fprintf(stderr, "\n");
6113 }
6114 fanalyzege(ge);
6115 fanalyzejoint(ge);
6116 fanalyzege(ge->bkwd);
6117 fanalyzejoint(ge->bkwd);
6118
6119 /* the results of this join will have to be reconsidered */
6120 startge = ge = ge->frwd;
6121 goto restart;
6122 } else {
6123 gbadcv++; gbadcvdots += ndots;
6124 }
6125 }
6126
6127 /* if we're down to 2 entries then the join has failed */
6128 if(pge->frwd == nge) {
6129 pgex->flags &= ~joinmask;
6130 if(ISDBG(FCONCISE))
6131 fprintf(stderr, "no match\n");
6132 goto next;
6133 }
6134
6135 /* reduce the number of entries by dropping one at some end,
6136 * should never drop the original ge from the range
6137 */
6138
6139 if(nge->bkwd == ge
6140 || pge != ge && (pgex->flags & GEXF_JCVMASK) <= (ngex->flags & GEXF_JCVMASK) ) {
6141 pge = pge->frwd;
6142 } else {
6143 nge = nge->bkwd;
6144 }
6145 if(ISDBG(FCONCISE))
6146 fprintf(stderr, "next try: %p to %p\n", pge, nge);
6147 }
6148
6149 next:
6150 ge = ge->frwd;
6151 if(ge == startge) {
6152 joinmask = (joinmask >> 1) & GEXF_JCVMASK;
6153 if(joinmask == 0)
6154 break;
6155 }
6156 }
6157
6158 /* join flat segments into lines */
6159 /* here ge==startge */
6160 while(1) {
6161 gex = X_CON(ge);
6162 if( !(gex->flags & GEXF_JLINE) )
6163 goto next2;
6164
6165 ndots = 0;
6166 dots[ndots].p[X] = ge->fx3;
6167 dots[ndots].p[Y] = ge->fy3;
6168 ndots++;
6169
6170 pge = ge->bkwd;
6171 nge = ge->frwd;
6172
6173 if(ISDBG(FCONCISE))
6174 fprintf(stderr, "joining LINE from %p-%p\n", ge, nge);
6175
6176 while(pge!=nge) {
6177 pgex = X_CON(pge);
6178 ngex = X_CON(nge);
6179 if(ISDBG(FCONCISE))
6180 fprintf(stderr, "(p=%p/%x n=0x%x/%x) ", pge, pgex->flags & GEXF_JLINE,
6181 nge, ngex->flags & GEXF_JLINE);
6182 if( !((pgex->flags | ngex->flags) & GEXF_JLINE) ) {
6183 if(ISDBG(FCONCISE))
6184 fprintf(stderr, "(end p=%p n=%p) ", pge, nge);
6185 break;
6186 }
6187
6188 if(maxdots < ndots+2) {
6189 maxdots += MAXDOTS;
6190 dots = (struct dot_dist *)realloc((void *)dots, sizeof(*dots)*maxdots);
6191 if(dots == NULL) {
6192 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
6193 exit(255);
6194 }
6195 }
6196 if( pgex->flags & GEXF_JLINE ) {
6197 for(i=0; i<2; i++) {
6198 apcv[0][i] = pge->bkwd->fpoints[i][2];
6199 apcv[1][i] = nge->fpoints[i][2];
6200 dots[ndots].p[i] = pge->fpoints[i][2];
6201 }
6202 ndots++;
6203 for(i=0; i<ndots; i++) {
6204 avsd2 = fdotsegdist2(apcv, dots[i].p);
6205 if(avsd2 > CVEPS2)
6206 break;
6207 }
6208 if(i<ndots) { /* failed to join */
6209 if(ISDBG(FCONCISE))
6210 fprintf(stderr, "failed to join prev %p ", pge);
6211 ndots--;
6212 pgex->flags &= ~GEXF_JLINE;
6213 } else {
6214 pge = pge->bkwd;
6215 if(pge == nge) {
6216 if(ISDBG(FCONCISE))
6217 fprintf(stderr, "intersected at prev %p ", pge);
6218 break; /* oops, tried to self-intersect */
6219 }
6220 }
6221 } else if(ISDBG(FCONCISE))
6222 fprintf(stderr, "(p=%p) ", pge);
6223
6224 if( ngex->flags & GEXF_JLINE ) {
6225 for(i=0; i<2; i++) {
6226 apcv[0][i] = pge->fpoints[i][2]; /* pge points before the 1st segment */
6227 apcv[1][i] = nge->frwd->fpoints[i][2];
6228 dots[ndots].p[i] = nge->fpoints[i][2];
6229 }
6230 ndots++;
6231 for(i=0; i<ndots; i++) {
6232 avsd2 = fdotsegdist2(apcv, dots[i].p);
6233 if(avsd2 > CVEPS2)
6234 break;
6235 }
6236 if(i<ndots) { /* failed to join */
6237 if(ISDBG(FCONCISE))
6238 fprintf(stderr, "failed to join next %p ", nge->frwd);
6239 ndots--;
6240 ngex->flags &= ~GEXF_JLINE;
6241 } else {
6242 nge = nge->frwd;
6243 }
6244 } else if(ISDBG(FCONCISE))
6245 fprintf(stderr, "(n=%p) ", nge);
6246 }
6247
6248 pge = pge->frwd; /* now the limits are pge...nge inclusive */
6249 if(pge == nge) /* a deeply perversive contour */
6250 break;
6251
6252 if(ISDBG(FCONCISE)) {
6253 fprintf(stderr, "\nin %s joined LINE %p-%p from\n", g->name, pge, nge);
6254 dumppaths(g, pge, nge);
6255 }
6256 pge->type = GE_LINE;
6257 for(i=0; i<2; i++) {
6258 pge->fpoints[i][2] = nge->fpoints[i][2];
6259 }
6260 fnormalizege(pge);
6261 X_CON_F(pge) &= ~GEXF_JLINE;
6262
6263 ge = pge;
6264 for(ige = pge->frwd; ; ige = pge->frwd) {
6265 if(ige == pge) {
6266 fprintf(stderr, "WARNING: assertion in %s line %d, please report it to the ttf2pt1 project\n",
6267 __FILE__, __LINE__);
6268 free(dots);
6269 return;
6270 }
6271 if(startge == ige)
6272 startge = pge;
6273 free(ige->ext);
6274 freethisge(ige);
6275 if(ige == nge)
6276 break;
6277 }
6278 next2:
6279 ge = ge->frwd;
6280 if(ge == startge)
6281 break;
6282 }
6283
6284 free(dots);
6285 }
6286
6287 /* force conciseness: substitute 2 or more curves going in the
6288 ** same quadrant with one curve
6289 ** in floating point
6290 */
6291
6292 void
fforceconcise(GLYPH * g)6293 fforceconcise(
6294 GLYPH * g
6295 )
6296 {
6297
6298 GENTRY *ge, *nge, *endge, *xge;
6299
6300 assertisfloat(g, "enforcing conciseness");
6301
6302 fdelsmall(g, 0.05);
6303 assertpath(g->entries, __FILE__, __LINE__, g->name);
6304
6305 if(ISDBG(FCONCISE))
6306 dumppaths(g, NULL, NULL);
6307
6308 /* collect more information about each gentry and their joints */
6309 for (ge = g->entries; ge != 0; ge = ge->next)
6310 if (ge->type == GE_CURVE || ge->type == GE_LINE)
6311 fnormalizege(ge);
6312
6313 for (ge = g->entries; ge != 0; ge = ge->next)
6314 if (ge->type == GE_CURVE || ge->type == GE_LINE) {
6315 alloc_gex_con(ge);
6316 fanalyzege(ge);
6317 }
6318
6319 /* see what we can do about joining */
6320 for (ge = g->entries; ge != 0; ge = ge->next)
6321 if (ge->type == GE_CURVE || ge->type == GE_LINE)
6322 fanalyzejoint(ge);
6323
6324 /* now do the joining */
6325 for (ge = g->entries; ge != 0; ge = ge->next)
6326 if(ge->type == GE_MOVE)
6327 fconcisecontour(g, ge->next);
6328
6329 for (ge = g->entries; ge != 0; ge = ge->next)
6330 if (ge->type == GE_CURVE || ge->type == GE_LINE)
6331 free(ge->ext);
6332 }
6333
6334 void
print_glyph(int glyphno)6335 print_glyph(
6336 int glyphno
6337 )
6338 {
6339 GLYPH *g;
6340 GENTRY *ge;
6341 int x = 0, y = 0;
6342 int i;
6343 int grp, lastgrp= -1;
6344
6345 if(ISDBG(FCONCISE) && glyphno == 0) {
6346 fprintf(stderr, "Guessed curves: bad %d/%d good %d/%d\n",
6347 gbadcv, gbadcvdots, ggoodcv, ggoodcvdots);
6348 }
6349
6350 g = &glyph_list[glyphno];
6351
6352 fprintf(pfa_file, "/%s { \n", g->name);
6353
6354 /* consider widths >MAXLEGALWIDTH as bugs */
6355 if( g->scaledwidth <= MAXLEGALWIDTH ) {
6356 fprintf(pfa_file, "0 %d hsbw\n", g->scaledwidth);
6357 } else {
6358 fprintf(pfa_file, "0 1000 hsbw\n");
6359 WARNING_2 fprintf(stderr, "glyph %s: width %d seems to be buggy, set to 1000\n",
6360 g->name, g->scaledwidth);
6361 }
6362
6363 #if 0
6364 fprintf(pfa_file, "%% contours: ");
6365 for (i = 0; i < g->ncontours; i++)
6366 fprintf(pfa_file, "%s(%d,%d) ", (g->contours[i].direction == DIR_OUTER ? "out" : "in"),
6367 g->contours[i].xofmin, g->contours[i].ymin);
6368 fprintf(pfa_file, "\n");
6369
6370 if (g->rymin < 5000)
6371 fprintf(pfa_file, "%d lower%s\n", g->rymin, (g->flatymin ? "flat" : "curve"));
6372 if (g->rymax > -5000)
6373 fprintf(pfa_file, "%d upper%s\n", g->rymax, (g->flatymax ? "flat" : "curve"));
6374 #endif
6375
6376 if (g->hstems)
6377 for (i = 0; i < g->nhs; i += 2) {
6378 if (g->hstems[i].flags & ST_3) {
6379 fprintf(pfa_file, "%d %d %d %d %d %d hstem3\n",
6380 g->hstems[i].value,
6381 g->hstems[i + 1].value - g->hstems[i].value,
6382 g->hstems[i + 2].value,
6383 g->hstems[i + 3].value - g->hstems[i + 2].value,
6384 g->hstems[i + 4].value,
6385 g->hstems[i + 5].value - g->hstems[i + 4].value
6386 );
6387 i += 4;
6388 } else {
6389 fprintf(pfa_file, "%d %d hstem\n", g->hstems[i].value,
6390 g->hstems[i + 1].value - g->hstems[i].value);
6391 }
6392 }
6393
6394 if (g->vstems)
6395 for (i = 0; i < g->nvs; i += 2) {
6396 if (g->vstems[i].flags & ST_3) {
6397 fprintf(pfa_file, "%d %d %d %d %d %d vstem3\n",
6398 g->vstems[i].value,
6399 g->vstems[i + 1].value - g->vstems[i].value,
6400 g->vstems[i + 2].value,
6401 g->vstems[i + 3].value - g->vstems[i + 2].value,
6402 g->vstems[i + 4].value,
6403 g->vstems[i + 5].value - g->vstems[i + 4].value
6404 );
6405 i += 4;
6406 } else {
6407 fprintf(pfa_file, "%d %d vstem\n", g->vstems[i].value,
6408 g->vstems[i + 1].value - g->vstems[i].value);
6409 }
6410 }
6411
6412 for (ge = g->entries; ge != 0; ge = ge->next) {
6413 if(g->nsg>0) {
6414 grp=ge->stemid;
6415 if(grp >= 0 && grp != lastgrp) {
6416 fprintf(pfa_file, "%d 4 callsubr\n", grp+g->firstsubr);
6417 lastgrp=grp;
6418 }
6419 }
6420
6421 switch (ge->type) {
6422 case GE_MOVE:
6423 if (absolute)
6424 fprintf(pfa_file, "%d %d amoveto\n", ge->ix3, ge->iy3);
6425 else
6426 rmoveto(ge->ix3 - x, ge->iy3 - y);
6427 if (0)
6428 fprintf(stderr, "Glyph %s: print moveto(%d, %d)\n",
6429 g->name, ge->ix3, ge->iy3);
6430 x = ge->ix3;
6431 y = ge->iy3;
6432 break;
6433 case GE_LINE:
6434 if (absolute)
6435 fprintf(pfa_file, "%d %d alineto\n", ge->ix3, ge->iy3);
6436 else
6437 rlineto(ge->ix3 - x, ge->iy3 - y);
6438 x = ge->ix3;
6439 y = ge->iy3;
6440 break;
6441 case GE_CURVE:
6442 if (absolute)
6443 fprintf(pfa_file, "%d %d %d %d %d %d arcurveto\n",
6444 ge->ix1, ge->iy1, ge->ix2, ge->iy2, ge->ix3, ge->iy3);
6445 else
6446 rrcurveto(ge->ix1 - x, ge->iy1 - y,
6447 ge->ix2 - ge->ix1, ge->iy2 - ge->iy1,
6448 ge->ix3 - ge->ix2, ge->iy3 - ge->iy2);
6449 x = ge->ix3;
6450 y = ge->iy3;
6451 break;
6452 case GE_PATH:
6453 closepath();
6454 break;
6455 default:
6456 WARNING_1 fprintf(stderr, "**** Glyph %s: unknown entry type '%c'\n",
6457 g->name, ge->type);
6458 break;
6459 }
6460 }
6461
6462 fprintf(pfa_file, "endchar } ND\n");
6463 }
6464
6465 /* print the subroutines for this glyph, returns the number of them */
6466 int
print_glyph_subs(int glyphno,int startid)6467 print_glyph_subs(
6468 int glyphno,
6469 int startid /* start numbering subroutines from this id */
6470 )
6471 {
6472 GLYPH *g;
6473 int i, grp;
6474
6475 g = &glyph_list[glyphno];
6476
6477 if(!hints || !subhints || g->nsg<1)
6478 return 0;
6479
6480 g->firstsubr=startid;
6481
6482 #if 0
6483 fprintf(pfa_file, "%% %s %d\n", g->name, g->nsg);
6484 #endif
6485 for(grp=0; grp<g->nsg; grp++) {
6486 fprintf(pfa_file, "dup %d {\n", startid++);
6487 for(i= (grp==0)? 0 : g->nsbs[grp-1]; i<g->nsbs[grp]; i++)
6488 fprintf(pfa_file, "\t%d %d %cstem\n", g->sbstems[i].low,
6489 g->sbstems[i].high-g->sbstems[i].low,
6490 g->sbstems[i].isvert ? 'v' : 'h');
6491 fprintf(pfa_file, "\treturn\n\t} NP\n");
6492 }
6493
6494 return g->nsg;
6495 }
6496
6497 void
print_glyph_metrics(int code,int glyphno)6498 print_glyph_metrics(
6499 int code,
6500 int glyphno
6501 )
6502 {
6503 GLYPH *g;
6504
6505 g = &glyph_list[glyphno];
6506
6507 if(transform)
6508 fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
6509 code, g->scaledwidth, g->name,
6510 iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax));
6511 else
6512 fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
6513 code, g->scaledwidth, g->name,
6514 g->xMin, g->yMin, g->xMax, g->yMax);
6515 }
6516
6517 /*
6518 SB:
6519 An important note about the BlueValues.
6520
6521 The Adobe documentation says that the maximal width of a Blue zone
6522 is connected to the value of BlueScale, which is by default 0.039625.
6523 The BlueScale value defines, at which point size the overshoot
6524 suppression be disabled.
6525
6526 The formula for it that is given in the manual is:
6527
6528 BlueScale=point_size/240, for a 300dpi device
6529
6530 that makes us wonder what is this 240 standing for. Incidentally
6531 240=72*1000/300, where 72 is the relation between inches and points,
6532 1000 is the size of the glyph matrix, and 300dpi is the resolution of
6533 the output device. Knowing that we can recalculate the formula for
6534 the font size in pixels rather than points:
6535
6536 BlueScale=pixel_size/1000
6537
6538 That looks a lot simpler than the original formula, does not it ?
6539 And the limitation about the maximal width of zone also looks
6540 a lot simpler after the transformation:
6541
6542 max_width < 1000/pixel_size
6543
6544 that ensures that even at the maximal pixel size when the overshoot
6545 suppression is disabled the zone width will be less than one pixel.
6546 This is important, failure to comply to this limit will result in
6547 really ugly fonts (been there, done that). But knowing the formula
6548 for the pixel width, we see that in fact we can use the maximal width
6549 of 24, not 23 as specified in the manual.
6550
6551 */
6552
6553 #define MAXBLUEWIDTH (24)
6554
6555 /*
6556 * Find the indexes of the most frequent values
6557 * in the hystogram, sort them in ascending order, and save which one
6558 * was the best one (if asked).
6559 * Returns the number of values found (may be less than maximal because
6560 * we ignore the zero values)
6561 */
6562
6563 #define MAXHYST (2000) /* size of the hystogram */
6564 #define HYSTBASE 500
6565
6566 static int
besthyst(int * hyst,int base,int * best,int nbest,int width,int * bestindp)6567 besthyst(
6568 int *hyst, /* the hystogram */
6569 int base, /* the base point of the hystogram */
6570 int *best, /* the array for indexes of best values */
6571 int nbest, /* its allocated size */
6572 int width, /* minimal difference between indexes */
6573 int *bestindp /* returned top point */
6574 )
6575 {
6576 unsigned char hused[MAXHYST / 8 + 1];
6577 int i, max, j, w, last = 0;
6578 int nf = 0;
6579
6580 width--;
6581
6582 memset(hused, 0 , sizeof hused);
6583
6584 max = 1;
6585 for (i = 0; i < nbest && max != 0; i++) {
6586 best[i] = 0;
6587 max = 0;
6588 for (j = 1; j < MAXHYST - 1; j++) {
6589 w = hyst[j];
6590
6591 if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) {
6592 best[i] = j;
6593 max = w;
6594 }
6595 }
6596 if (max != 0) {
6597 if (max < last/2) {
6598 /* do not pick the too low values */
6599 break;
6600 }
6601 for (j = best[i] - width; j <= best[i] + width; j++) {
6602 if (j >= 0 && j < MAXHYST)
6603 hused[j >> 3] |= (1 << (j & 0x07));
6604 }
6605 last = max;
6606 best[i] -= base;
6607 nf = i + 1;
6608 }
6609 }
6610
6611 if (bestindp)
6612 *bestindp = best[0];
6613
6614 /* sort the indexes in ascending order */
6615 for (i = 0; i < nf; i++) {
6616 for (j = i + 1; j < nf; j++)
6617 if (best[j] < best[i]) {
6618 w = best[i];
6619 best[i] = best[j];
6620 best[j] = w;
6621 }
6622 }
6623
6624 return nf;
6625 }
6626
6627 /*
6628 * Find the next best Blue zone in the hystogram.
6629 * Return the weight of the found zone.
6630 */
6631
6632 static int
bestblue(short * zhyst,short * physt,short * ozhyst,int * bluetab)6633 bestblue(
6634 short *zhyst, /* the zones hystogram */
6635 short *physt, /* the points hystogram */
6636 short *ozhyst, /* the other zones hystogram */
6637 int *bluetab /* where to put the found zone */
6638 )
6639 {
6640 int i, j, w, max, ind, first, last;
6641
6642 /* find the highest point in the zones hystogram */
6643 /* if we have a plateau, take its center */
6644 /* if we have multiple peaks, take the first one */
6645
6646 max = -1;
6647 first = last = -10;
6648 for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6649 w = zhyst[i];
6650 if (w > max) {
6651 first = last = i;
6652 max = w;
6653 } else if (w == max) {
6654 if (last == i - 1)
6655 last = i;
6656 }
6657 }
6658 ind = (first + last) / 2;
6659
6660 if (max == 0) /* no zones left */
6661 return 0;
6662
6663 /* now we reuse `first' and `last' as inclusive borders of the zone */
6664 first = ind;
6665 last = ind + (MAXBLUEWIDTH - 1);
6666
6667 /* our maximal width is far too big, so we try to make it narrower */
6668 w = max;
6669 j = (w & 1); /* a pseudo-random bit */
6670 while (1) {
6671 while (physt[first] == 0)
6672 first++;
6673 while (physt[last] == 0)
6674 last--;
6675 if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max)
6676 break;
6677
6678 if (physt[first] < physt[last]
6679 || physt[first] == physt[last] && j) {
6680 if (physt[first] * 20 > w) /* if weight is >5%,
6681 * stop */
6682 break;
6683 w -= physt[first];
6684 first++;
6685 j = 0;
6686 } else {
6687 if (physt[last] * 20 > w) /* if weight is >5%,
6688 * stop */
6689 break;
6690 w -= physt[last];
6691 last--;
6692 j = 1;
6693 }
6694 }
6695
6696 /* save our zone */
6697 bluetab[0] = first - HYSTBASE;
6698 bluetab[1] = last - HYSTBASE;
6699
6700 /* invalidate all the zones overlapping with this one */
6701 /* the constant of 2 is determined by the default value of BlueFuzz */
6702 for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++)
6703 if (i >= 0 && i < MAXHYST) {
6704 zhyst[i] = 0;
6705 ozhyst[i] = 0;
6706 }
6707 return w;
6708 }
6709
6710 /*
6711 * Try to find the Blue Values, bounding box and italic angle
6712 */
6713
6714 void
findblues(void)6715 findblues(void)
6716 {
6717 /* hystograms for upper and lower zones */
6718 short hystl[MAXHYST];
6719 short hystu[MAXHYST];
6720 short zuhyst[MAXHYST];
6721 short zlhyst[MAXHYST];
6722 int nchars;
6723 int i, j, k, w, max;
6724 GENTRY *ge;
6725 GLYPH *g;
6726 double ang;
6727
6728 /* find the lowest and highest points of glyphs */
6729 /* and by the way build the values for FontBBox */
6730 /* and build the hystogram for the ItalicAngle */
6731
6732 /* re-use hystl for the hystogram of italic angle */
6733
6734 bbox[0] = bbox[1] = 5000;
6735 bbox[2] = bbox[3] = -5000;
6736
6737 for (i = 0; i < MAXHYST; i++)
6738 hystl[i] = 0;
6739
6740 nchars = 0;
6741
6742 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6743 if (g->flags & GF_USED) {
6744 nchars++;
6745
6746 g->rymin = 5000;
6747 g->rymax = -5000;
6748 for (ge = g->entries; ge != 0; ge = ge->next) {
6749 if (ge->type == GE_LINE) {
6750
6751 j = ge->iy3 - ge->prev->iy3;
6752 k = ge->ix3 - ge->prev->ix3;
6753 if (j > 0)
6754 ang = atan2(-k, j) * 180.0 / M_PI;
6755 else
6756 ang = atan2(k, -j) * 180.0 / M_PI;
6757
6758 k /= 100;
6759 j /= 100;
6760 if (ang > -45.0 && ang < 45.0) {
6761 /*
6762 * be careful to not overflow
6763 * the counter
6764 */
6765 hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4;
6766 }
6767 if (ge->iy3 == ge->prev->iy3) {
6768 if (ge->iy3 <= g->rymin) {
6769 g->rymin = ge->iy3;
6770 g->flatymin = 1;
6771 }
6772 if (ge->iy3 >= g->rymax) {
6773 g->rymax = ge->iy3;
6774 g->flatymax = 1;
6775 }
6776 } else {
6777 if (ge->iy3 < g->rymin) {
6778 g->rymin = ge->iy3;
6779 g->flatymin = 0;
6780 }
6781 if (ge->iy3 > g->rymax) {
6782 g->rymax = ge->iy3;
6783 g->flatymax = 0;
6784 }
6785 }
6786 } else if (ge->type == GE_CURVE) {
6787 if (ge->iy3 < g->rymin) {
6788 g->rymin = ge->iy3;
6789 g->flatymin = 0;
6790 }
6791 if (ge->iy3 > g->rymax) {
6792 g->rymax = ge->iy3;
6793 g->flatymax = 0;
6794 }
6795 }
6796 if (ge->type == GE_LINE || ge->type == GE_CURVE) {
6797 if (ge->ix3 < bbox[0])
6798 bbox[0] = ge->ix3;
6799 if (ge->ix3 > bbox[2])
6800 bbox[2] = ge->ix3;
6801 if (ge->iy3 < bbox[1])
6802 bbox[1] = ge->iy3;
6803 if (ge->iy3 > bbox[3])
6804 bbox[3] = ge->iy3;
6805 }
6806 }
6807 }
6808 }
6809
6810 /* get the most popular angle */
6811 max = 0;
6812 w = 0;
6813 for (i = 0; i < MAXHYST; i++) {
6814 if (hystl[i] > w) {
6815 w = hystl[i];
6816 max = i;
6817 }
6818 }
6819 ang = (double) (max - HYSTBASE) / 10.0;
6820 WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang);
6821 if (italic_angle == 0.0)
6822 italic_angle = ang;
6823
6824 /* build the hystogram of the lower points */
6825 for (i = 0; i < MAXHYST; i++)
6826 hystl[i] = 0;
6827
6828 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6829 if ((g->flags & GF_USED)
6830 && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) {
6831 hystl[g->rymin + HYSTBASE]++;
6832 }
6833 }
6834
6835 /* build the hystogram of the upper points */
6836 for (i = 0; i < MAXHYST; i++)
6837 hystu[i] = 0;
6838
6839 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6840 if ((g->flags & GF_USED)
6841 && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) {
6842 hystu[g->rymax + HYSTBASE]++;
6843 }
6844 }
6845
6846 /* build the hystogram of all the possible lower zones with max width */
6847 for (i = 0; i < MAXHYST; i++)
6848 zlhyst[i] = 0;
6849
6850 for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6851 for (j = 0; j < MAXBLUEWIDTH; j++)
6852 zlhyst[i] += hystl[i + j];
6853 }
6854
6855 /* build the hystogram of all the possible upper zones with max width */
6856 for (i = 0; i < MAXHYST; i++)
6857 zuhyst[i] = 0;
6858
6859 for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6860 for (j = 0; j < MAXBLUEWIDTH; j++)
6861 zuhyst[i] += hystu[i + j];
6862 }
6863
6864 /* find the baseline */
6865 w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]);
6866 if (0)
6867 fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars,
6868 bluevalues[0], bluevalues[1]);
6869
6870 if (w == 0) /* no baseline, something weird */
6871 return;
6872
6873 /* find the upper zones */
6874 for (nblues = 2; nblues < 14; nblues += 2) {
6875 w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]);
6876
6877 if (0)
6878 fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars,
6879 bluevalues[nblues], bluevalues[nblues+1]);
6880
6881 if (w * 20 < nchars)
6882 break; /* don't save this zone */
6883 }
6884
6885 /* find the lower zones */
6886 for (notherb = 0; notherb < 10; notherb += 2) {
6887 w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]);
6888
6889 if (0)
6890 fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars,
6891 otherblues[notherb], otherblues[notherb+1]);
6892
6893
6894 if (w * 20 < nchars)
6895 break; /* don't save this zone */
6896 }
6897
6898 }
6899
6900 /*
6901 * Find the actual width of the glyph and modify the
6902 * description to reflect it. Not guaranteed to do
6903 * any good, may make character spacing too wide.
6904 */
6905
6906 void
docorrectwidth(void)6907 docorrectwidth(void)
6908 {
6909 int i;
6910 GENTRY *ge;
6911 GLYPH *g;
6912 int xmin, xmax;
6913 int maxwidth, minsp;
6914
6915 /* enforce this minimal spacing,
6916 * we limit the amount of the enforced spacing to avoid
6917 * spacing the bold wonts too widely
6918 */
6919 minsp = (stdhw>60 || stdhw<10)? 60 : stdhw;
6920
6921 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6922 g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */
6923
6924 if (correctwidth && g->flags & GF_USED) {
6925 xmin = 5000;
6926 xmax = -5000;
6927 for (ge = g->entries; ge != 0; ge = ge->next) {
6928 if (ge->type != GE_LINE && ge->type != GE_CURVE)
6929 continue;
6930
6931 if (ge->ix3 <= xmin) {
6932 xmin = ge->ix3;
6933 }
6934 if (ge->ix3 >= xmax) {
6935 xmax = ge->ix3;
6936 }
6937 }
6938
6939 maxwidth=xmax+minsp;
6940 if( g->scaledwidth < maxwidth ) {
6941 g->scaledwidth = maxwidth;
6942 WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n",
6943 g->name, g->oldwidth, g->scaledwidth );
6944 }
6945 }
6946 }
6947
6948 }
6949
6950 /*
6951 * Try to find the typical stem widths
6952 */
6953
6954 void
stemstatistics(void)6955 stemstatistics(void)
6956 {
6957 #define MINDIST 10 /* minimal distance between the widths */
6958 int hyst[MAXHYST+MINDIST*2];
6959 int best[12];
6960 int i, j, k, w;
6961 int nchars;
6962 int ns;
6963 STEM *s;
6964 GLYPH *g;
6965
6966 /* start with typical stem width */
6967
6968 nchars=0;
6969
6970 /* build the hystogram of horizontal stem widths */
6971 memset(hyst, 0, sizeof hyst);
6972
6973 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6974 if (g->flags & GF_USED) {
6975 nchars++;
6976 s = g->hstems;
6977 for (j = 0; j < g->nhs; j += 2) {
6978 if ((s[j].flags | s[j + 1].flags) & ST_END)
6979 continue;
6980 w = s[j + 1].value - s[j].value+1;
6981 if(w==20) /* split stems should not be counted */
6982 continue;
6983 if (w > 0 && w < MAXHYST - 1) {
6984 /*
6985 * handle some fuzz present in
6986 * converted fonts
6987 */
6988 hyst[w+MINDIST] += MINDIST-1;
6989 for(k=1; k<MINDIST-1; k++) {
6990 hyst[w+MINDIST + k] += MINDIST-1-k;
6991 hyst[w+MINDIST - k] += MINDIST-1-k;
6992 }
6993 }
6994 }
6995 }
6996 }
6997
6998 /* find 12 most frequent values */
6999 ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdhw);
7000
7001 /* store data in stemsnaph */
7002 for (i = 0; i < ns; i++)
7003 stemsnaph[i] = best[i];
7004 if (ns < 12)
7005 stemsnaph[ns] = 0;
7006
7007 /* build the hystogram of vertical stem widths */
7008 memset(hyst, 0, sizeof hyst);
7009
7010 for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
7011 if (g->flags & GF_USED) {
7012 s = g->vstems;
7013 for (j = 0; j < g->nvs; j += 2) {
7014 if ((s[j].flags | s[j + 1].flags) & ST_END)
7015 continue;
7016 w = s[j + 1].value - s[j].value+1;
7017 if (w > 0 && w < MAXHYST - 1) {
7018 /*
7019 * handle some fuzz present in
7020 * converted fonts
7021 */
7022 hyst[w+MINDIST] += MINDIST-1;
7023 for(k=1; k<MINDIST-1; k++) {
7024 hyst[w+MINDIST + k] += MINDIST-1-k;
7025 hyst[w+MINDIST - k] += MINDIST-1-k;
7026 }
7027 }
7028 }
7029 }
7030 }
7031
7032 /* find 12 most frequent values */
7033 ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdvw);
7034
7035 /* store data in stemsnaph */
7036 for (i = 0; i < ns; i++)
7037 stemsnapv[i] = best[i];
7038 if (ns < 12)
7039 stemsnapv[ns] = 0;
7040
7041 #undef MINDIST
7042 }
7043
7044 /*
7045 * SB
7046 * A funny thing: TTF paths are going in reverse direction compared
7047 * to Type1. So after all (because the rest of logic uses TTF
7048 * path directions) we have to reverse the paths.
7049 *
7050 * It was a big headache to discover that.
7051 */
7052
7053 /* works on both int and float paths */
7054
7055 void
reversepathsfromto(GENTRY * from,GENTRY * to)7056 reversepathsfromto(
7057 GENTRY * from,
7058 GENTRY * to
7059 )
7060 {
7061 GENTRY *ge, *nge, *pge;
7062 GENTRY *cur, *next;
7063 int i, n, ilast[2];
7064 double flast[2], f;
7065
7066 for (ge = from; ge != 0 && ge != to; ge = ge->next) {
7067 if(ge->type == GE_LINE || ge->type == GE_CURVE) {
7068 if (ISDBG(REVERSAL))
7069 fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd);
7070
7071 /* cut out the path itself */
7072 pge = ge->prev; /* GE_MOVE */
7073 if (pge == 0) {
7074 fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n");
7075 exit(1);
7076 }
7077 nge = ge->bkwd->next; /* GE_PATH */
7078 pge->next = nge;
7079 nge->prev = pge;
7080 ge->bkwd->next = 0; /* mark end of chain */
7081
7082 /* remember the starting point */
7083 if(ge->flags & GEF_FLOAT) {
7084 flast[0] = pge->fx3;
7085 flast[1] = pge->fy3;
7086 } else {
7087 ilast[0] = pge->ix3;
7088 ilast[1] = pge->iy3;
7089 }
7090
7091 /* then reinsert them in backwards order */
7092 for(cur = ge; cur != 0; cur = next ) {
7093 next = cur->next; /* or addgeafter() will screw it up */
7094 if(cur->flags & GEF_FLOAT) {
7095 for(i=0; i<2; i++) {
7096 /* reverse the direction of path element */
7097 f = cur->fpoints[i][0];
7098 cur->fpoints[i][0] = cur->fpoints[i][1];
7099 cur->fpoints[i][1] = f;
7100 f = flast[i];
7101 flast[i] = cur->fpoints[i][2];
7102 cur->fpoints[i][2] = f;
7103 }
7104 } else {
7105 for(i=0; i<2; i++) {
7106 /* reverse the direction of path element */
7107 n = cur->ipoints[i][0];
7108 cur->ipoints[i][0] = cur->ipoints[i][1];
7109 cur->ipoints[i][1] = n;
7110 n = ilast[i];
7111 ilast[i] = cur->ipoints[i][2];
7112 cur->ipoints[i][2] = n;
7113 }
7114 }
7115 addgeafter(pge, cur);
7116 }
7117
7118 /* restore the starting point */
7119 if(ge->flags & GEF_FLOAT) {
7120 pge->fx3 = flast[0];
7121 pge->fy3 = flast[1];
7122 } else {
7123 pge->ix3 = ilast[0];
7124 pge->iy3 = ilast[1];
7125 }
7126
7127 ge = nge;
7128 }
7129
7130 }
7131 }
7132
7133 void
reversepaths(GLYPH * g)7134 reversepaths(
7135 GLYPH * g
7136 )
7137 {
7138 reversepathsfromto(g->entries, NULL);
7139 }
7140
7141 /* add a kerning pair information, scales the value */
7142
7143 void
addkernpair(unsigned id1,unsigned id2,int unscval)7144 addkernpair(
7145 unsigned id1,
7146 unsigned id2,
7147 int unscval
7148 )
7149 {
7150 static unsigned char *bits = 0;
7151 static int lastid;
7152 GLYPH *g = &glyph_list[id1];
7153 int i, n;
7154 struct kern *p;
7155
7156 if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs)
7157 return;
7158
7159 if( (glyph_list[id1].flags & GF_USED)==0
7160 || (glyph_list[id2].flags & GF_USED)==0 )
7161 return;
7162
7163 if(bits == 0) {
7164 bits = calloc( BITMAP_BYTES(numglyphs), 1);
7165 if (bits == NULL) {
7166 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
7167 exit(255);
7168 }
7169 lastid = id1;
7170 }
7171
7172 if(lastid != id1) {
7173 /* refill the bitmap cache */
7174 memset(bits, 0,BITMAP_BYTES(numglyphs));
7175 p = g->kern;
7176 for(i=g->kerncount; i>0; i--) {
7177 n = (p++)->id;
7178 SET_BITMAP(bits, n);
7179 }
7180 lastid = id1;
7181 }
7182
7183 if(IS_BITMAP(bits, id2))
7184 return; /* duplicate */
7185
7186 if(g->kerncount <= g->kernalloc) {
7187 g->kernalloc += 8;
7188 p = realloc(g->kern, sizeof(struct kern) * g->kernalloc);
7189 if(p == 0) {
7190 fprintf (stderr, "** realloc failed, kerning data will be incomplete\n");
7191 }
7192 g->kern = p;
7193 }
7194
7195 SET_BITMAP(bits, id2);
7196 p = &g->kern[g->kerncount];
7197 p->id = id2;
7198 p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth);
7199 g->kerncount++;
7200 kerning_pairs++;
7201 }
7202
7203 /* print out the kerning information */
7204
7205 void
print_kerning(FILE * afm_file)7206 print_kerning(
7207 FILE *afm_file
7208 )
7209 {
7210 int i, j, n;
7211 GLYPH *g;
7212 struct kern *p;
7213
7214 if( kerning_pairs == 0 )
7215 return;
7216
7217 fprintf(afm_file, "StartKernData\n");
7218 fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs);
7219
7220 for(i=0; i<numglyphs; i++) {
7221 g = &glyph_list[i];
7222 if( (g->flags & GF_USED) ==0)
7223 continue;
7224 p = g->kern;
7225 for(j=g->kerncount; j>0; j--, p++) {
7226 fprintf(afm_file, "KPX %s %s %d\n", g->name,
7227 glyph_list[ p->id ].name, p->val );
7228 }
7229 }
7230
7231 fprintf(afm_file, "EndKernPairs\n");
7232 fprintf(afm_file, "EndKernData\n");
7233 }
7234
7235
7236 #if 0
7237
7238 /*
7239 ** This function is commented out because the information
7240 ** collected by it is not used anywhere else yet. Now
7241 ** it only collects the directions of contours. And the
7242 ** direction of contours gets fixed already in draw_glyf().
7243 **
7244 ***********************************************
7245 **
7246 ** Here we expect that the paths are already closed.
7247 ** We also expect that the contours do not intersect
7248 ** and that curves doesn't cross any border of quadrant.
7249 **
7250 ** Find which contours go inside which and what is
7251 ** their proper direction. Then fix the direction
7252 ** to make it right.
7253 **
7254 */
7255
7256 #define MAXCONT 1000
7257
7258 void
7259 fixcontours(
7260 GLYPH * g
7261 )
7262 {
7263 CONTOUR cont[MAXCONT];
7264 short ymax[MAXCONT]; /* the highest point */
7265 short xofmax[MAXCONT]; /* X-coordinate of any point
7266 * at ymax */
7267 short ymin[MAXCONT]; /* the lowest point */
7268 short xofmin[MAXCONT]; /* X-coordinate of any point
7269 * at ymin */
7270 short count[MAXCONT]; /* count of lines */
7271 char dir[MAXCONT]; /* in which direction they must go */
7272 GENTRY *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT];
7273 int ncont;
7274 int i;
7275 int dx1, dy1, dx2, dy2;
7276 GENTRY *ge, *nge;
7277
7278 /* find the contours and their most upper/lower points */
7279 ncont = 0;
7280 ymax[0] = -5000;
7281 ymin[0] = 5000;
7282 for (ge = g->entries; ge != 0; ge = ge->next) {
7283 if (ge->type == GE_LINE || ge->type == GE_CURVE) {
7284 if (ge->iy3 > ymax[ncont]) {
7285 ymax[ncont] = ge->iy3;
7286 xofmax[ncont] = ge->ix3;
7287 maxptr[ncont] = ge;
7288 }
7289 if (ge->iy3 < ymin[ncont]) {
7290 ymin[ncont] = ge->iy3;
7291 xofmin[ncont] = ge->ix3;
7292 minptr[ncont] = ge;
7293 }
7294 }
7295 if (ge->frwd != ge->next) {
7296 start[ncont++] = ge->frwd;
7297 ymax[ncont] = -5000;
7298 ymin[ncont] = 5000;
7299 }
7300 }
7301
7302 /* determine the directions of contours */
7303 for (i = 0; i < ncont; i++) {
7304 ge = minptr[i];
7305 nge = ge->frwd;
7306
7307 if (ge->type == GE_CURVE) {
7308 dx1 = ge->ix3 - ge->ix2;
7309 dy1 = ge->iy3 - ge->iy2;
7310
7311 if (dx1 == 0 && dy1 == 0) { /* a pathological case */
7312 dx1 = ge->ix3 - ge->ix1;
7313 dy1 = ge->iy3 - ge->iy1;
7314 }
7315 if (dx1 == 0 && dy1 == 0) { /* a more pathological
7316 * case */
7317 dx1 = ge->ix3 - ge->prev->ix3;
7318 dy1 = ge->iy3 - ge->prev->iy3;
7319 }
7320 } else {
7321 dx1 = ge->ix3 - ge->prev->ix3;
7322 dy1 = ge->iy3 - ge->prev->iy3;
7323 }
7324 if (nge->type == GE_CURVE) {
7325 dx2 = ge->ix3 - nge->ix1;
7326 dy2 = ge->iy3 - nge->iy1;
7327 if (dx1 == 0 && dy1 == 0) { /* a pathological case */
7328 dx2 = ge->ix3 - nge->ix2;
7329 dy2 = ge->iy3 - nge->iy2;
7330 }
7331 if (dx1 == 0 && dy1 == 0) { /* a more pathological
7332 * case */
7333 dx2 = ge->ix3 - nge->ix3;
7334 dy2 = ge->iy3 - nge->iy3;
7335 }
7336 } else {
7337 dx2 = ge->ix3 - nge->ix3;
7338 dy2 = ge->iy3 - nge->iy3;
7339 }
7340
7341 /* compare angles */
7342 cont[i].direction = DIR_INNER;
7343 if (dy1 == 0) {
7344 if (dx1 < 0)
7345 cont[i].direction = DIR_OUTER;
7346 } else if (dy2 == 0) {
7347 if (dx2 > 0)
7348 cont[i].direction = DIR_OUTER;
7349 } else if (dx2 * dy1 < dx1 * dy2)
7350 cont[i].direction = DIR_OUTER;
7351
7352 cont[i].ymin = ymin[i];
7353 cont[i].xofmin = xofmin[i];
7354 }
7355
7356 /* save the information that may be needed further */
7357 g->ncontours = ncont;
7358 if (ncont > 0) {
7359 g->contours = malloc(sizeof(CONTOUR) * ncont);
7360 if (g->contours == 0) {
7361 fprintf(stderr, "***** Memory allocation error *****\n");
7362 exit(255);
7363 }
7364 memcpy(g->contours, cont, sizeof(CONTOUR) * ncont);
7365 }
7366 }
7367
7368 #endif
7369
7370 /*
7371 *
7372 */
7373
7374