source: trunk/filemanager/tp/dompdf/lib/ttf2ufm/ttf2ufm-src/pt1.c @ 2000

Revision 2000, 181.7 KB checked in by amuller, 14 years ago (diff)

Ticket #597 - Implementação do módulo gerenciador de arquivos

Line 
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
36struct 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};
93typedef 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 */
108static int ggoodcv=0, ggoodcvdots=0, gbadcv=0, gbadcvdots=0;
109
110int      stdhw, stdvw;  /* dominant stems widths */
111int      stemsnaph[12], stemsnapv[12];  /* most typical stem width */
112
113int      bluevalues[14];
114int      nblues;
115int      otherblues[10];
116int      notherb;
117int      bbox[4];       /* the FontBBox array */
118double   italic_angle;
119
120GLYPH   *glyph_list;
121int    encoding[ENCTABSZ];      /* inverse of glyph[].char_no */
122int    kerning_pairs = 0;
123
124/* prototypes */
125static void fixcvdir( GENTRY * ge, int dir);
126static void fixcvends( GENTRY * ge);
127static int fgetcvdir( GENTRY * ge);
128static int igetcvdir( GENTRY * ge);
129static int fiszigzag( GENTRY *ge);
130static int iiszigzag( GENTRY *ge);
131static GENTRY * freethisge( GENTRY *ge);
132static void addgeafter( GENTRY *oge, GENTRY *nge );
133static GENTRY * newgentry( int flags);
134static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs);
135static int addbluestems( STEM *s, int n);
136static void sortstems( STEM * s, int n);
137static int stemoverlap( STEM * s1, STEM * s2);
138static int steminblue( STEM *s);
139static void markbluestems( STEM *s, int nold);
140static int joinmainstems( STEM * s, int nold, int useblues);
141static void joinsubstems( STEM * s, short *pairs, int nold, int useblues);
142static void fixendpath( GENTRY *ge);
143static void fdelsmall( GLYPH *g, double minlen);
144static void alloc_gex_con( GENTRY *ge);
145static double fjointsin2( GENTRY *ge1, GENTRY *ge2);
146static double fcvarea( GENTRY *ge);
147static double fcvval( GENTRY *ge, int axis, double t);
148static void fsampledots( GENTRY *ge, double dots[][2], int ndots);
149static void fnormalizege( GENTRY *ge);
150static void fanalyzege( GENTRY *ge);
151static void fanalyzejoint( GENTRY *ge);
152static void fconcisecontour( GLYPH *g, GENTRY *ge);
153static double fclosegap( GENTRY *from, GENTRY *to, int axis,
154        double gap, double *ret);
155
156int
157isign(
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
169int
170fsign(
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
182static GENTRY *
183newgentry(
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
205void
206rmoveto(
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
219void
220rlineto(
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
236void
237rrcurveto(
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
262void
263closepath(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
282void
283assertpath(
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
352void
353assertisfloat(
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
370void
371assertisint(
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
393void
394fg_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
439void
440ig_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
483void
484fg_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
529void
530ig_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
573void
574fg_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
637void
638ig_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
698void
699g_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
751static void
752fixcvends(
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
901static void
902fixcvdir(
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
1033static int
1034fgetcvdir(
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
1095static int
1096igetcvdir(
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() */
1156static void
1157testfixcvdir(
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
1173static int
1174iround(
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
1186void
1187dumppaths(
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
1238void
1239pathtoint(
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
1328int
1329fcheckcv(
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
1354int
1355icheckcv(
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
1384void
1385fclosepaths(
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
1449void
1450smoothjoints(
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 */
1705static void
1706debugstems(
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 */
1736static int
1737addbluestems(
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 */
1784static void
1785sortstems(
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
1836static int
1837stemoverlap(
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
1861static int
1862steminblue(
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
1890static void
1891markbluestems(
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 */
1983static int
1984joinmainstems(
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 */
2515static short xbstem[MAX_STEMS*2];
2516/* index of the last point */
2517static 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 */
2524static int
2525subfrombase(
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 */
2648static void
2649printbasestem(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 */
2663static void
2664joinsubstems(
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 */
2807static void
2808uniformstems(
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 */
2863static int
2864findstemat(
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
2949static int gssentry_lastgrp=0; /* reset to 0 for each new glyph */
2950
2951static int
2952gssentry( /* 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
3221static void
3222groupsubstems(
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
3328void
3329buildstems(
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
3832void
3833fstraighten(
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
3963static int
3964fsqequation(
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
4040void
4041ffixquadrants(
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
4189static int
4190iiszigzag(
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
4239static int
4240fiszigzag(
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
4289void
4290fsplitzigzags(
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
4357static GENTRY *
4358freethisge(
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
4425static void
4426addgeafter(
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
4472static void
4473fixendpath(
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
4501static double
4502fclosegap(
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
4619static void
4620fdelsmall(
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
4828struct 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};
4834static 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
4840static int
4841fcrossraysxx(
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
4908int
4909fcrossrayscv(
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
4934int
4935fcrossraysge(
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 */
4968static
4969printdot(
4970        double dot[2]
4971)
4972{
4973        fprintf(stderr, "(%g,%g)", dot[0], dot[1]);
4974}
4975
4976static
4977printseg(
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
4994double
4995fdotsegdist2(
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
5086double
5087fdotcurvdist2(
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
5268void
5269fapproxcurve(
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
5439static double
5440fjointsin2(
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
5480static double
5481fcvarea(
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
5512static double
5513fcvval(
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
5536static void
5537fsampledots(
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
5569static void
5570alloc_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
5586static void
5587fnormalizege(
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
5639static void
5640fanalyzege(
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
5713static void
5714fanalyzejoint(
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
5757try_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
5781try_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
5816try_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
5834static void
5835fconcisecontour(
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
6149next:
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                }
6278next2:
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
6292void
6293fforceconcise(
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
6334void
6335print_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 */
6466int
6467print_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
6497void
6498print_glyph_metrics(
6499     FILE *afm_file,
6500           int code,
6501           int glyphno
6502)
6503{
6504        GLYPH *g;
6505
6506        g = &glyph_list[glyphno];
6507
6508        if(transform)
6509          fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
6510                  code, g->scaledwidth, g->name,
6511                  iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax));
6512        else
6513          fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
6514                  code, g->scaledwidth, g->name,
6515                  g->xMin, g->yMin, g->xMax, g->yMax);
6516
6517}
6518
6519void
6520print_glyph_metrics_ufm(
6521     FILE *ufm_file,
6522           int code,
6523           int glyphno
6524)
6525{
6526        GLYPH *g;
6527
6528        g = &glyph_list[glyphno];
6529
6530  // OAR - added bounding box for Unicode glyphs
6531  fprintf(ufm_file, "U %d ; WX %d ; N %s ; G %d ; B %d %d %d %d ;\n",
6532          code, g->scaledwidth, g->name, glyphno, g->xMin, g->yMin, g->xMax, g->yMax);
6533}
6534/*
6535 SB:
6536 An important note about the BlueValues.
6537
6538 The Adobe documentation says that the maximal width of a Blue zone
6539 is connected to the value of BlueScale, which is by default 0.039625.
6540 The BlueScale value defines, at which point size the overshoot
6541 suppression be disabled.
6542
6543 The formula for it that is given in the manual is:
6544
6545  BlueScale=point_size/240, for a 300dpi device
6546
6547 that makes us wonder what is this 240 standing for. Incidentally
6548 240=72*1000/300, where 72 is the relation between inches and points,
6549 1000 is the size of the glyph matrix, and 300dpi is the resolution of
6550 the output device. Knowing that we can recalculate the formula for
6551 the font size in pixels rather than points:
6552
6553  BlueScale=pixel_size/1000
6554
6555 That looks a lot simpler than the original formula, does not it ?
6556 And the limitation about the maximal width of zone also looks
6557 a lot simpler after the transformation:
6558
6559  max_width < 1000/pixel_size
6560
6561 that ensures that even at the maximal pixel size when the overshoot
6562 suppression is disabled the zone width will be less than one pixel.
6563 This is important, failure to comply to this limit will result in
6564 really ugly fonts (been there, done that). But knowing the formula
6565 for the pixel width, we see that in fact we can use the maximal width
6566 of 24, not 23 as specified in the manual.
6567
6568*/
6569
6570#define MAXBLUEWIDTH (24)
6571
6572/*
6573 * Find the indexes of the most frequent values
6574 * in the hystogram, sort them in ascending order, and save which one
6575 * was the best one (if asked).
6576 * Returns the number of values found (may be less than maximal because
6577 * we ignore the zero values)
6578 */
6579
6580#define MAXHYST (2000)          /* size of the hystogram */
6581#define HYSTBASE 500
6582
6583static int
6584besthyst(
6585         int *hyst,             /* the hystogram */
6586         int base,              /* the base point of the hystogram */
6587         int *best,             /* the array for indexes of best values */
6588         int nbest,             /* its allocated size */
6589         int width,             /* minimal difference between indexes */
6590         int *bestindp          /* returned top point */
6591)
6592{
6593        unsigned char   hused[MAXHYST / 8 + 1];
6594        int             i, max, j, w, last = 0;
6595        int             nf = 0;
6596
6597        width--;
6598
6599        memset(hused, 0 , sizeof hused);
6600
6601        max = 1;
6602        for (i = 0; i < nbest && max != 0; i++) {
6603                best[i] = 0;
6604                max = 0;
6605                for (j = 1; j < MAXHYST - 1; j++) {
6606                        w = hyst[j];
6607
6608                        if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) {
6609                                best[i] = j;
6610                                max = w;
6611                        }
6612                }
6613                if (max != 0) {
6614                        if (max < last/2) {
6615                                /* do not pick the too low values */
6616                                break;
6617                        }
6618                        for (j = best[i] - width; j <= best[i] + width; j++) {
6619                                if (j >= 0 && j < MAXHYST)
6620                                        hused[j >> 3] |= (1 << (j & 0x07));
6621                        }
6622                        last = max;
6623                        best[i] -= base;
6624                        nf = i + 1;
6625                }
6626        }
6627
6628        if (bestindp)
6629                *bestindp = best[0];
6630
6631        /* sort the indexes in ascending order */
6632        for (i = 0; i < nf; i++) {
6633                for (j = i + 1; j < nf; j++)
6634                        if (best[j] < best[i]) {
6635                                w = best[i];
6636                                best[i] = best[j];
6637                                best[j] = w;
6638                        }
6639        }
6640
6641        return nf;
6642}
6643
6644/*
6645 * Find the next best Blue zone in the hystogram.
6646 * Return the weight of the found zone.
6647 */
6648
6649static int
6650bestblue(
6651         short *zhyst,          /* the zones hystogram */
6652         short *physt,          /* the points hystogram */
6653         short *ozhyst,         /* the other zones hystogram */
6654         int *bluetab           /* where to put the found zone */
6655)
6656{
6657        int             i, j, w, max, ind, first, last;
6658
6659        /* find the highest point in the zones hystogram */
6660        /* if we have a plateau, take its center */
6661        /* if we have multiple peaks, take the first one */
6662
6663        max = -1;
6664        first = last = -10;
6665        for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6666                w = zhyst[i];
6667                if (w > max) {
6668                        first = last = i;
6669                        max = w;
6670                } else if (w == max) {
6671                        if (last == i - 1)
6672                                last = i;
6673                }
6674        }
6675        ind = (first + last) / 2;
6676
6677        if (max == 0)           /* no zones left */
6678                return 0;
6679
6680        /* now we reuse `first' and `last' as inclusive borders of the zone */
6681        first = ind;
6682        last = ind + (MAXBLUEWIDTH - 1);
6683
6684        /* our maximal width is far too big, so we try to make it narrower */
6685        w = max;
6686        j = (w & 1);            /* a pseudo-random bit */
6687        while (1) {
6688                while (physt[first] == 0)
6689                        first++;
6690                while (physt[last] == 0)
6691                        last--;
6692                if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max)
6693                        break;
6694
6695                if (physt[first] < physt[last]
6696                    || physt[first] == physt[last] && j) {
6697                        if (physt[first] * 20 > w)      /* if weight is >5%,
6698                                                         * stop */
6699                                break;
6700                        w -= physt[first];
6701                        first++;
6702                        j = 0;
6703                } else {
6704                        if (physt[last] * 20 > w)       /* if weight is >5%,
6705                                                         * stop */
6706                                break;
6707                        w -= physt[last];
6708                        last--;
6709                        j = 1;
6710                }
6711        }
6712
6713        /* save our zone */
6714        bluetab[0] = first - HYSTBASE;
6715        bluetab[1] = last - HYSTBASE;
6716
6717        /* invalidate all the zones overlapping with this one */
6718        /* the constant of 2 is determined by the default value of BlueFuzz */
6719        for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++)
6720                if (i >= 0 && i < MAXHYST) {
6721                        zhyst[i] = 0;
6722                        ozhyst[i] = 0;
6723                }
6724        return w;
6725}
6726
6727/*
6728 * Try to find the Blue Values, bounding box and italic angle
6729 */
6730
6731void
6732findblues(void)
6733{
6734        /* hystograms for upper and lower zones */
6735        short           hystl[MAXHYST];
6736        short           hystu[MAXHYST];
6737        short           zuhyst[MAXHYST];
6738        short           zlhyst[MAXHYST];
6739        int             nchars;
6740        int             i, j, k, w, max;
6741        GENTRY         *ge;
6742        GLYPH          *g;
6743        double          ang;
6744
6745        /* find the lowest and highest points of glyphs */
6746        /* and by the way build the values for FontBBox */
6747        /* and build the hystogram for the ItalicAngle */
6748
6749        /* re-use hystl for the hystogram of italic angle */
6750
6751        bbox[0] = bbox[1] = 5000;
6752        bbox[2] = bbox[3] = -5000;
6753
6754        for (i = 0; i < MAXHYST; i++)
6755                hystl[i] = 0;
6756
6757        nchars = 0;
6758
6759        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6760                if (g->flags & GF_USED) {
6761                        nchars++;
6762
6763                        g->rymin = 5000;
6764                        g->rymax = -5000;
6765                        for (ge = g->entries; ge != 0; ge = ge->next) {
6766                                if (ge->type == GE_LINE) {
6767
6768                                        j = ge->iy3 - ge->prev->iy3;
6769                                        k = ge->ix3 - ge->prev->ix3;
6770                                        if (j > 0)
6771                                                ang = atan2(-k, j) * 180.0 / M_PI;
6772                                        else
6773                                                ang = atan2(k, -j) * 180.0 / M_PI;
6774
6775                                        k /= 100;
6776                                        j /= 100;
6777                                        if (ang > -45.0 && ang < 45.0) {
6778                                                /*
6779                                                 * be careful to not overflow
6780                                                 * the counter
6781                                                 */
6782                                                hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4;
6783                                        }
6784                                        if (ge->iy3 == ge->prev->iy3) {
6785                                                if (ge->iy3 <= g->rymin) {
6786                                                        g->rymin = ge->iy3;
6787                                                        g->flatymin = 1;
6788                                                }
6789                                                if (ge->iy3 >= g->rymax) {
6790                                                        g->rymax = ge->iy3;
6791                                                        g->flatymax = 1;
6792                                                }
6793                                        } else {
6794                                                if (ge->iy3 < g->rymin) {
6795                                                        g->rymin = ge->iy3;
6796                                                        g->flatymin = 0;
6797                                                }
6798                                                if (ge->iy3 > g->rymax) {
6799                                                        g->rymax = ge->iy3;
6800                                                        g->flatymax = 0;
6801                                                }
6802                                        }
6803                                } else if (ge->type == GE_CURVE) {
6804                                        if (ge->iy3 < g->rymin) {
6805                                                g->rymin = ge->iy3;
6806                                                g->flatymin = 0;
6807                                        }
6808                                        if (ge->iy3 > g->rymax) {
6809                                                g->rymax = ge->iy3;
6810                                                g->flatymax = 0;
6811                                        }
6812                                }
6813                                if (ge->type == GE_LINE || ge->type == GE_CURVE) {
6814                                        if (ge->ix3 < bbox[0])
6815                                                bbox[0] = ge->ix3;
6816                                        if (ge->ix3 > bbox[2])
6817                                                bbox[2] = ge->ix3;
6818                                        if (ge->iy3 < bbox[1])
6819                                                bbox[1] = ge->iy3;
6820                                        if (ge->iy3 > bbox[3])
6821                                                bbox[3] = ge->iy3;
6822                                }
6823                        }
6824                }
6825        }
6826
6827        /* get the most popular angle */
6828        max = 0;
6829        w = 0;
6830        for (i = 0; i < MAXHYST; i++) {
6831                if (hystl[i] > w) {
6832                        w = hystl[i];
6833                        max = i;
6834                }
6835        }
6836        ang = (double) (max - HYSTBASE) / 10.0;
6837        WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang);
6838        if (italic_angle == 0.0)
6839                italic_angle = ang;
6840
6841        /* build the hystogram of the lower points */
6842        for (i = 0; i < MAXHYST; i++)
6843                hystl[i] = 0;
6844
6845        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6846                if ((g->flags & GF_USED)
6847                    && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) {
6848                        hystl[g->rymin + HYSTBASE]++;
6849                }
6850        }
6851
6852        /* build the hystogram of the upper points */
6853        for (i = 0; i < MAXHYST; i++)
6854                hystu[i] = 0;
6855
6856        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6857                if ((g->flags & GF_USED)
6858                    && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) {
6859                        hystu[g->rymax + HYSTBASE]++;
6860                }
6861        }
6862
6863        /* build the hystogram of all the possible lower zones with max width */
6864        for (i = 0; i < MAXHYST; i++)
6865                zlhyst[i] = 0;
6866
6867        for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6868                for (j = 0; j < MAXBLUEWIDTH; j++)
6869                        zlhyst[i] += hystl[i + j];
6870        }
6871
6872        /* build the hystogram of all the possible upper zones with max width */
6873        for (i = 0; i < MAXHYST; i++)
6874                zuhyst[i] = 0;
6875
6876        for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
6877                for (j = 0; j < MAXBLUEWIDTH; j++)
6878                        zuhyst[i] += hystu[i + j];
6879        }
6880
6881        /* find the baseline */
6882        w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]);
6883        if (0)
6884                fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars,
6885                                bluevalues[0], bluevalues[1]);
6886
6887        if (w == 0)             /* no baseline, something weird */
6888                return;
6889
6890        /* find the upper zones */
6891        for (nblues = 2; nblues < 14; nblues += 2) {
6892                w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]);
6893
6894                if (0)
6895                        fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars,
6896                                bluevalues[nblues], bluevalues[nblues+1]);
6897
6898                if (w * 20 < nchars)
6899                        break;  /* don't save this zone */
6900        }
6901
6902        /* find the lower zones */
6903        for (notherb = 0; notherb < 10; notherb += 2) {
6904                w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]);
6905
6906                if (0)
6907                        fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars,
6908                                otherblues[notherb], otherblues[notherb+1]);
6909
6910
6911                if (w * 20 < nchars)
6912                        break;  /* don't save this zone */
6913        }
6914
6915}
6916
6917/*
6918 * Find the actual width of the glyph and modify the
6919 * description to reflect it. Not guaranteed to do
6920 * any good, may make character spacing too wide.
6921 */
6922
6923void
6924docorrectwidth(void)
6925{
6926        int             i;
6927        GENTRY         *ge;
6928        GLYPH          *g;
6929        int             xmin, xmax;
6930        int             maxwidth, minsp;
6931
6932        /* enforce this minimal spacing,
6933         * we limit the amount of the enforced spacing to avoid
6934         * spacing the bold wonts too widely
6935         */
6936        minsp = (stdhw>60 || stdhw<10)? 60 : stdhw;
6937
6938        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6939                g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */
6940
6941                if (correctwidth && g->flags & GF_USED) {
6942                        xmin = 5000;
6943                        xmax = -5000;
6944                        for (ge = g->entries; ge != 0; ge = ge->next) {
6945                                if (ge->type != GE_LINE && ge->type != GE_CURVE)
6946                                        continue;
6947
6948                                if (ge->ix3 <= xmin) {
6949                                        xmin = ge->ix3;
6950                                }
6951                                if (ge->ix3 >= xmax) {
6952                                        xmax = ge->ix3;
6953                                }
6954                        }
6955
6956                        maxwidth=xmax+minsp;
6957                        if( g->scaledwidth < maxwidth ) {
6958                                g->scaledwidth = maxwidth;
6959                                WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n",
6960                                        g->name, g->oldwidth, g->scaledwidth );
6961                        }
6962                }
6963        }
6964
6965}
6966
6967/*
6968 * Try to find the typical stem widths
6969 */
6970
6971void
6972stemstatistics(void)
6973{
6974#define MINDIST 10 /* minimal distance between the widths */
6975        int             hyst[MAXHYST+MINDIST*2];
6976        int             best[12];
6977        int             i, j, k, w;
6978        int             nchars;
6979        int             ns;
6980        STEM           *s;
6981        GLYPH          *g;
6982
6983        /* start with typical stem width */
6984
6985        nchars=0;
6986
6987        /* build the hystogram of horizontal stem widths */
6988        memset(hyst, 0, sizeof hyst);
6989
6990        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
6991                if (g->flags & GF_USED) {
6992                        nchars++;
6993                        s = g->hstems;
6994                        for (j = 0; j < g->nhs; j += 2) {
6995                                if ((s[j].flags | s[j + 1].flags) & ST_END)
6996                                        continue;
6997                                w = s[j + 1].value - s[j].value+1;
6998                                if(w==20) /* split stems should not be counted */
6999                                        continue;
7000                                if (w > 0 && w < MAXHYST - 1) {
7001                                        /*
7002                                         * handle some fuzz present in
7003                                         * converted fonts
7004                                         */
7005                                        hyst[w+MINDIST] += MINDIST-1;
7006                                        for(k=1; k<MINDIST-1; k++) {
7007                                                hyst[w+MINDIST + k] += MINDIST-1-k;
7008                                                hyst[w+MINDIST - k] += MINDIST-1-k;
7009                                        }
7010                                }
7011                        }
7012                }
7013        }
7014
7015        /* find 12 most frequent values */
7016        ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdhw);
7017
7018        /* store data in stemsnaph */
7019        for (i = 0; i < ns; i++)
7020                stemsnaph[i] = best[i];
7021        if (ns < 12)
7022                stemsnaph[ns] = 0;
7023
7024        /* build the hystogram of vertical stem widths */
7025        memset(hyst, 0, sizeof hyst);
7026
7027        for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
7028                if (g->flags & GF_USED) {
7029                        s = g->vstems;
7030                        for (j = 0; j < g->nvs; j += 2) {
7031                                if ((s[j].flags | s[j + 1].flags) & ST_END)
7032                                        continue;
7033                                w = s[j + 1].value - s[j].value+1;
7034                                if (w > 0 && w < MAXHYST - 1) {
7035                                        /*
7036                                         * handle some fuzz present in
7037                                         * converted fonts
7038                                         */
7039                                        hyst[w+MINDIST] += MINDIST-1;
7040                                        for(k=1; k<MINDIST-1; k++) {
7041                                                hyst[w+MINDIST + k] += MINDIST-1-k;
7042                                                hyst[w+MINDIST - k] += MINDIST-1-k;
7043                                        }
7044                                }
7045                        }
7046                }
7047        }
7048
7049        /* find 12 most frequent values */
7050        ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdvw);
7051
7052        /* store data in stemsnaph */
7053        for (i = 0; i < ns; i++)
7054                stemsnapv[i] = best[i];
7055        if (ns < 12)
7056                stemsnapv[ns] = 0;
7057
7058#undef MINDIST
7059}
7060
7061/*
7062 * SB
7063 * A funny thing: TTF paths are going in reverse direction compared
7064 * to Type1. So after all (because the rest of logic uses TTF
7065 * path directions) we have to reverse the paths.
7066 *
7067 * It was a big headache to discover that.
7068 */
7069
7070/* works on both int and float paths */
7071
7072void
7073reversepathsfromto(
7074                   GENTRY * from,
7075                   GENTRY * to
7076)
7077{
7078        GENTRY         *ge, *nge, *pge;
7079        GENTRY         *cur, *next;
7080        int i, n, ilast[2];
7081        double flast[2], f;
7082
7083        for (ge = from; ge != 0 && ge != to; ge = ge->next) {
7084                if(ge->type == GE_LINE || ge->type == GE_CURVE) {
7085                        if (ISDBG(REVERSAL))
7086                                fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd);
7087
7088                        /* cut out the path itself */
7089                        pge = ge->prev; /* GE_MOVE */
7090                        if (pge == 0) {
7091                                fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n");
7092                                exit(1);
7093                        }
7094                        nge = ge->bkwd->next; /* GE_PATH */
7095                        pge->next = nge;
7096                        nge->prev = pge;
7097                        ge->bkwd->next = 0; /* mark end of chain */
7098
7099                        /* remember the starting point */
7100                        if(ge->flags & GEF_FLOAT) {
7101                                flast[0] = pge->fx3;
7102                                flast[1] = pge->fy3;
7103                        } else {
7104                                ilast[0] = pge->ix3;
7105                                ilast[1] = pge->iy3;
7106                        }
7107
7108                        /* then reinsert them in backwards order */
7109                        for(cur = ge; cur != 0; cur = next ) {
7110                                next = cur->next; /* or addgeafter() will screw it up */
7111                                if(cur->flags & GEF_FLOAT) {
7112                                        for(i=0; i<2; i++) {
7113                                                /* reverse the direction of path element */
7114                                                f = cur->fpoints[i][0];
7115                                                cur->fpoints[i][0] = cur->fpoints[i][1];
7116                                                cur->fpoints[i][1] = f;
7117                                                f = flast[i];
7118                                                flast[i] = cur->fpoints[i][2];
7119                                                cur->fpoints[i][2] = f;
7120                                        }
7121                                } else {
7122                                        for(i=0; i<2; i++) {
7123                                                /* reverse the direction of path element */
7124                                                n = cur->ipoints[i][0];
7125                                                cur->ipoints[i][0] = cur->ipoints[i][1];
7126                                                cur->ipoints[i][1] = n;
7127                                                n = ilast[i];
7128                                                ilast[i] = cur->ipoints[i][2];
7129                                                cur->ipoints[i][2] = n;
7130                                        }
7131                                }
7132                                addgeafter(pge, cur);
7133                        }
7134
7135                        /* restore the starting point */
7136                        if(ge->flags & GEF_FLOAT) {
7137                                pge->fx3 = flast[0];
7138                                pge->fy3 = flast[1];
7139                        } else {
7140                                pge->ix3 = ilast[0];
7141                                pge->iy3 = ilast[1];
7142                        }
7143
7144                        ge = nge;
7145                }
7146
7147        }
7148}
7149
7150void
7151reversepaths(
7152             GLYPH * g
7153)
7154{
7155        reversepathsfromto(g->entries, NULL);
7156}
7157
7158/* add a kerning pair information, scales the value */
7159
7160void
7161addkernpair(
7162        unsigned id1,
7163        unsigned id2,
7164        int unscval
7165)
7166{
7167        static unsigned char *bits = 0;
7168        static int lastid;
7169        GLYPH *g = &glyph_list[id1];
7170        int i, n;
7171        struct kern *p;
7172
7173        if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs)
7174                return;
7175
7176        if( (glyph_list[id1].flags & GF_USED)==0
7177        || (glyph_list[id2].flags & GF_USED)==0 )
7178                return;
7179
7180        if(bits == 0) {
7181                bits = calloc( BITMAP_BYTES(numglyphs), 1);
7182                if (bits == NULL) {
7183                        fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
7184                        exit(255);
7185                }
7186                lastid = id1;
7187        }
7188
7189        if(lastid != id1) {
7190                /* refill the bitmap cache */
7191                memset(bits, 0,BITMAP_BYTES(numglyphs));
7192                p = g->kern;
7193                for(i=g->kerncount; i>0; i--) {
7194                        n = (p++)->id;
7195                        SET_BITMAP(bits, n);
7196                }
7197                lastid = id1;
7198        }
7199
7200        if(IS_BITMAP(bits, id2))
7201                return; /* duplicate */
7202
7203        if(g->kerncount <= g->kernalloc) {
7204                g->kernalloc += 8;
7205                p = realloc(g->kern, sizeof(struct kern) * g->kernalloc);
7206                if(p == 0) {
7207                        fprintf (stderr, "** realloc failed, kerning data will be incomplete\n");
7208                }
7209                g->kern = p;
7210        }
7211
7212        SET_BITMAP(bits, id2);
7213        p = &g->kern[g->kerncount];
7214        p->id = id2;
7215        p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth);
7216        g->kerncount++;
7217        kerning_pairs++;
7218}
7219
7220/* print out the kerning information */
7221
7222void
7223print_kerning(
7224        FILE *afm_file
7225)
7226{
7227        int     i, j, n;
7228        GLYPH *g;
7229        struct kern *p;
7230
7231        if( kerning_pairs == 0 )
7232                return;
7233
7234        fprintf(afm_file, "StartKernData\n");
7235        fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs);
7236
7237        for(i=0; i<numglyphs; i++)  {
7238                g = &glyph_list[i];
7239                if( (g->flags & GF_USED) ==0)
7240                        continue;
7241                p = g->kern;
7242                for(j=g->kerncount; j>0; j--, p++) {
7243                        fprintf(afm_file, "KPX %s %s %d\n", g->name,
7244                                glyph_list[ p->id ].name, p->val );
7245                }
7246        }
7247
7248        fprintf(afm_file, "EndKernPairs\n");
7249        fprintf(afm_file, "EndKernData\n");
7250}
7251
7252
7253#if 0
7254
7255/*
7256** This function is commented out because the information
7257** collected by it is not used anywhere else yet. Now
7258** it only collects the directions of contours. And the
7259** direction of contours gets fixed already in draw_glyf().
7260**
7261***********************************************
7262**
7263** Here we expect that the paths are already closed.
7264** We also expect that the contours do not intersect
7265** and that curves doesn't cross any border of quadrant.
7266**
7267** Find which contours go inside which and what is
7268** their proper direction. Then fix the direction
7269** to make it right.
7270**
7271*/
7272
7273#define MAXCONT 1000
7274
7275void
7276fixcontours(
7277            GLYPH * g
7278)
7279{
7280        CONTOUR         cont[MAXCONT];
7281        short           ymax[MAXCONT];  /* the highest point */
7282        short           xofmax[MAXCONT];        /* X-coordinate of any point
7283                                                 * at ymax */
7284        short           ymin[MAXCONT];  /* the lowest point */
7285        short           xofmin[MAXCONT];        /* X-coordinate of any point
7286                                                 * at ymin */
7287        short           count[MAXCONT]; /* count of lines */
7288        char            dir[MAXCONT];   /* in which direction they must go */
7289        GENTRY         *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT];
7290        int             ncont;
7291        int             i;
7292        int             dx1, dy1, dx2, dy2;
7293        GENTRY         *ge, *nge;
7294
7295        /* find the contours and their most upper/lower points */
7296        ncont = 0;
7297        ymax[0] = -5000;
7298        ymin[0] = 5000;
7299        for (ge = g->entries; ge != 0; ge = ge->next) {
7300                if (ge->type == GE_LINE || ge->type == GE_CURVE) {
7301                        if (ge->iy3 > ymax[ncont]) {
7302                                ymax[ncont] = ge->iy3;
7303                                xofmax[ncont] = ge->ix3;
7304                                maxptr[ncont] = ge;
7305                        }
7306                        if (ge->iy3 < ymin[ncont]) {
7307                                ymin[ncont] = ge->iy3;
7308                                xofmin[ncont] = ge->ix3;
7309                                minptr[ncont] = ge;
7310                        }
7311                }
7312                if (ge->frwd != ge->next) {
7313                        start[ncont++] = ge->frwd;
7314                        ymax[ncont] = -5000;
7315                        ymin[ncont] = 5000;
7316                }
7317        }
7318
7319        /* determine the directions of contours */
7320        for (i = 0; i < ncont; i++) {
7321                ge = minptr[i];
7322                nge = ge->frwd;
7323
7324                if (ge->type == GE_CURVE) {
7325                        dx1 = ge->ix3 - ge->ix2;
7326                        dy1 = ge->iy3 - ge->iy2;
7327
7328                        if (dx1 == 0 && dy1 == 0) {     /* a pathological case */
7329                                dx1 = ge->ix3 - ge->ix1;
7330                                dy1 = ge->iy3 - ge->iy1;
7331                        }
7332                        if (dx1 == 0 && dy1 == 0) {     /* a more pathological
7333                                                         * case */
7334                                dx1 = ge->ix3 - ge->prev->ix3;
7335                                dy1 = ge->iy3 - ge->prev->iy3;
7336                        }
7337                } else {
7338                        dx1 = ge->ix3 - ge->prev->ix3;
7339                        dy1 = ge->iy3 - ge->prev->iy3;
7340                }
7341                if (nge->type == GE_CURVE) {
7342                        dx2 = ge->ix3 - nge->ix1;
7343                        dy2 = ge->iy3 - nge->iy1;
7344                        if (dx1 == 0 && dy1 == 0) {     /* a pathological case */
7345                                dx2 = ge->ix3 - nge->ix2;
7346                                dy2 = ge->iy3 - nge->iy2;
7347                        }
7348                        if (dx1 == 0 && dy1 == 0) {     /* a more pathological
7349                                                         * case */
7350                                dx2 = ge->ix3 - nge->ix3;
7351                                dy2 = ge->iy3 - nge->iy3;
7352                        }
7353                } else {
7354                        dx2 = ge->ix3 - nge->ix3;
7355                        dy2 = ge->iy3 - nge->iy3;
7356                }
7357
7358                /* compare angles */
7359                cont[i].direction = DIR_INNER;
7360                if (dy1 == 0) {
7361                        if (dx1 < 0)
7362                                cont[i].direction = DIR_OUTER;
7363                } else if (dy2 == 0) {
7364                        if (dx2 > 0)
7365                                cont[i].direction = DIR_OUTER;
7366                } else if (dx2 * dy1 < dx1 * dy2)
7367                        cont[i].direction = DIR_OUTER;
7368
7369                cont[i].ymin = ymin[i];
7370                cont[i].xofmin = xofmin[i];
7371        }
7372
7373        /* save the information that may be needed further */
7374        g->ncontours = ncont;
7375        if (ncont > 0) {
7376                g->contours = malloc(sizeof(CONTOUR) * ncont);
7377                if (g->contours == 0) {
7378                        fprintf(stderr, "***** Memory allocation error *****\n");
7379                        exit(255);
7380                }
7381                memcpy(g->contours, cont, sizeof(CONTOUR) * ncont);
7382        }
7383}
7384
7385#endif
7386
7387/*
7388 *
7389 */
7390
Note: See TracBrowser for help on using the repository browser.