]> git.sesse.net Git - rdpsrv/blob - Xserver/lib/font/Type1/paths.c
Support RDP5 logon packets.
[rdpsrv] / Xserver / lib / font / Type1 / paths.c
1 /* $XConsortium: paths.c,v 1.4 91/10/10 11:18:40 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
3  * All Rights Reserved
4  * Copyright Lexmark International, Inc. 1991
5  * All Rights Reserved
6  *
7  * License to use, copy, modify, and distribute this software and its
8  * documentation for any purpose and without fee is hereby granted,
9  * provided that the above copyright notice appear in all copies and that
10  * both that copyright notice and this permission notice appear in
11  * supporting documentation, and that the name of IBM or Lexmark not be
12  * used in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.
14  *
15  * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
16  * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
17  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
18  * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
19  * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
20  * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
21  * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
22  * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
23  * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
24  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
25  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
26  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
27  * THIS SOFTWARE.
28  */
29  /* PATHS    CWEB         V0021 ********                             */
30 /*
31 :h1 id=paths.PATHS Module - Path Operator Handler
32  
33 This is the module that is responsible for building and transforming
34 path lists.
35  
36 &author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
37  
38  
39 :h3.Include Files
40  
41 The included files are:
42 */
43  
44                              /*   after the system includes  (dsr)           */
45 #include  "objects.h"
46 #include  "spaces.h"
47 #include  "paths.h"
48 #include  "regions.h"      /* understands about Union                      */
49 #include  "fonts.h"        /* understands about TEXTTYPEs                  */
50 #include  "pictures.h"     /* understands about handles                    */
51 #include  "strokes.h"      /* understands how to coerce stroke paths       */
52 #include  "trig.h"
53 static UnClose();
54
55 /*
56 :h3.Routines Available to the TYPE1IMAGER User
57  
58 The PATHS routines that are made available to the outside user are:
59 */
60  
61 /*SHARED LINE(S) ORIGINATED HERE*/
62 /*
63 :h3.Functions Provided to Other Modules
64  
65 The path routines that are made available to other TYPE1IMAGER modules
66 are defined here:
67 */
68  
69 /*SHARED LINE(S) ORIGINATED HERE*/
70 /*
71 NOTE:  because of the casts put in the macros for Loc, ArcCA, Conic,
72 RoundConic, PathSegment, and JoinSegment, we cannot use the macro names
73 when the functions are actually defined.  We have to use the unique
74 names with their unique first two characters.  Thus, if anyone in the
75 future ever decided to change the first two characters, it would not be
76 enough just to change the macro (as it would for most other functions).
77 He would have to also change the function definition.
78 */
79 /*
80 :h3.Macros Provided to Other Modules
81  
82 The CONCAT macro is defined here and used in the STROKES module.  See
83 :hdref refid=pathmac..
84 */
85  
86 /*SHARED LINE(S) ORIGINATED HERE*/
87  
88 /*
89 :h2.Path Segment Structures
90  
91 A path is represented as a linked list of the following structure:
92 */
93  
94 /*SHARED LINE(S) ORIGINATED HERE*/
95 /*
96 When 'link' is NULL, we are at the last segment in the path (surprise!).
97  
98 'last' is only non-NULL on the first segment of a path,
99 for all the other segments 'last' == NULL.  We test for a non-NULL
100 'last' (ISPATHANCHOR predicate) when we are given an alleged path
101 to make sure the user is not trying to pull a fast one on us.
102  
103 A path may be a collection of disjoint paths.  Every break in the
104 disjoint path is represented by a MOVETYPE segment.
105  
106 Closed paths are discussed in :hdref refid=close..
107  
108 :h3.CopyPath() - Physically Duplicating a Path
109  
110 This simple function illustrates moving through the path linked list.
111 Duplicating a segment just involves making a copy of it, except for
112 text, which has some auxilliary things involved.  We don't feel
113 competent to duplicate text in this module, so we call someone who
114 knows how (in the FONTS module).
115 */
116 struct segment *CopyPath(p0)
117        register struct segment *p0;  /* path to duplicate                    */
118 {
119        register struct segment *p,*n,*last,*anchor;
120  
121        for (p = p0, anchor = NULL; p != NULL; p = p->link) {
122  
123                ARGCHECK((!ISPATHTYPE(p->type) || (p != p0 && p->last != NULL)),
124                        "CopyPath: invalid segment", p, NULL, (0), struct segment *);
125  
126                if (p->type == TEXTTYPE)
127                        n = (struct segment *) CopyText(p);
128                else
129                        n = (struct segment *)Allocate(p->size, p, 0);
130                n->last = NULL;
131                if (anchor == NULL)
132                        anchor = n;
133                else
134                        last->link = n;
135                last = n;
136        }
137 /*
138 At this point we have a chain of newly allocated segments hanging off
139 'anchor'.  We need to make sure the first segment points to the last:
140 */
141        if (anchor != NULL) {
142                n->link = NULL;
143                anchor->last = n;
144        }
145  
146        return(anchor);
147 }
148 /*
149 :h3.KillPath() - Destroying a Path
150  
151 Destroying a path is simply a matter of freeing each segment in the
152 linked list.  Again, we let the experts handle text.
153 */
154 void KillPath(p)
155        register struct segment *p;  /* path to destroy                       */
156 {
157        register struct segment *linkp;  /* temp register holding next segment*/
158  
159        /* return conditional based on reference count 3-26-91 PNM */
160        if ( (--(p->references) > 1) ||
161           ( (p->references == 1) && !ISPERMANENT(p->flag) ) )
162            return;
163  
164        while (p != NULL) {
165                if (!ISPATHTYPE(p->type)) {
166                        ArgErr("KillPath: bad segment", p, NULL);
167                        return;
168                }
169                linkp = p->link;
170                if (p->type == TEXTTYPE)
171                        KillText(p);
172                else
173                        Free(p);
174                p = linkp;
175        }
176 }
177  
178 /*
179 :h2 id=location."location" Objects
180  
181 The TYPE1IMAGER user creates and destroys objects of type "location".  These
182 objects locate points for the primitive path operators.  We play a trick
183 here and store these objects in the same "segment" structure used for
184 paths, with a type field == MOVETYPE.
185  
186 This allows the Line() operator, for example, to be very trivial:
187 It merely stamps its input structure as a LINETYPE and returns it to the
188 caller--assuming, of course, the input structure was not permanent (as
189 it usually isn't).
190  
191 :h3.The "movesegment" Template Structure
192  
193 This template is used as a generic segment structure for Allocate:
194 */
195  
196 /* added reference field 1 to temporary template below 3-26-91 PNM */
197 static struct segment movetemplate = { MOVETYPE, 0, 1, sizeof(struct segment), 0,
198                 NULL, NULL, 0, 0 };
199 /*
200 :h3.Loc() - Create an "Invisible Line" Between (0,0) and a Point
201  
202 */
203  
204 struct segment *t1_Loc(S, x, y)
205        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
206        double x,y;           /* destination point                            */
207 {
208        register struct segment *r;
209  
210  
211        IfTrace3((MustTraceCalls),"..Loc(S=%z, x=%f, y=%f)\n", S, &x, &y);
212  
213        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
214        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
215  
216        r->last = r;
217        r->context = S->context;
218        (*S->convert)(&r->dest, S, x, y);
219        ConsumeSpace(S);
220        return(r);
221 }
222 /*
223 :h3.ILoc() - Loc() With Integer Arguments
224  
225 */
226 struct segment *ILoc(S, x, y)
227        register struct XYspace *S;  /* coordinate space to interpret X,Y     */
228        register int x,y;        /* destination point                         */
229 {
230        register struct segment *r;
231  
232        IfTrace3((MustTraceCalls),"..ILoc(S=%z, x=%d, y=%d)\n",
233                                     S, (long) x, (long) y);
234        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
235        TYPECHECK("Loc", S, SPACETYPE, r, (0), struct segment *);
236  
237        r->last = r;
238        r->context = S->context;
239        (*S->iconvert)(&r->dest, S, (long) x, (long) y);
240        ConsumeSpace(S);
241        return(r);
242 }
243  
244 /*
245 :h3.SubLoc() - Vector Subtraction of Two Locition Objects
246  
247 This user operator subtracts two location objects, yielding a new
248 location object that is the result.
249  
250 The symmetrical function AddLoc() is totally redundent with Join(),
251 so it is not provided.
252 */
253  
254 struct segment *SubLoc(p1, p2)
255        register struct segment *p1;
256        register struct segment *p2;
257 {
258        IfTrace2((MustTraceCalls),"SubLoc(%z, %z)\n", p1, p2);
259  
260        ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0), struct segment *);
261        ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0), struct segment *);
262        p1 = UniquePath(p1);
263        p1->dest.x -= p2->dest.x;
264        p1->dest.y -= p2->dest.y;
265        ConsumePath(p2);
266        return(p1);
267 }
268  
269 /*
270 :h2.Straight Line Segments
271  
272 :h3.PathSegment() - Create a Generic Path Segment
273  
274 Many routines need a LINETYPE or MOVETYPE path segment, but do not
275 want to go through the external user's interface, because, for example,
276 they already know the "fractpel" destination of the segment and the
277 conversion is unnecessary.  PathSegment() is an internal routine
278 provided to the rest of TYPE1IMAGER for handling these cases.
279 */
280  
281 struct segment *t1_PathSegment(type, x, y)
282        int type;             /* LINETYPE or MOVETYPE                         */
283        fractpel x,y;         /* where to go to, if known                     */
284 {
285        register struct segment *r;  /* newly created segment                 */
286  
287        r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
288        r->type = type;
289        r->last = r;          /* last points to itself for singleton          */
290        r->dest.x = x;
291        r->dest.y = y;
292        return(r);
293 }
294 /*
295 :h3.Line() - Create a Line Segment Between (0,0) and a Point P
296  
297 This involves just creating and filling out a segment structure:
298 */
299 struct segment *Line(P)
300        register struct segment *P;  /* relevant coordinate space             */
301 {
302  
303        IfTrace1((MustTraceCalls),"..Line(%z)\n", P);
304        ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0), struct segment *);
305  
306        P = UniquePath(P);
307        P->type = LINETYPE;
308        return(P);
309 }
310 /*
311 :h2.Curved Path Segments
312  
313 We need more points to describe curves.  So, the structures for curved
314 path segments are slightly different.  The first part is identical;
315 the curved structures are larger with the extra points on the end.
316  
317 :h3.Bezier Segment Structure
318  
319 We support third order Bezier curves.  They are specified with four
320 control points A, B, C, and D.  The curve starts at A with slope AB
321 and ends at D with slope CD.  The curvature at the point A is inversely
322 related to the length |AB|, and the curvature at the point D is
323 inversely related to the length |CD|.  Point A is always point (0,0).
324  
325 */
326  
327 /*SHARED LINE(S) ORIGINATED HERE*/
328 /*
329 :h3.Bezier() - Generate a Bezier Segment
330  
331 This is just a simple matter of filling out a 'beziersegment' structure:
332 */
333  
334 struct beziersegment *Bezier(B, C, D)
335        register struct segment *B;  /* second control point                  */
336        register struct segment *C;  /* third control point                   */
337        register struct segment *D;  /* fourth control point (ending point)   */
338 {
339 /* added reference field of 1 to temporary template below 3-26-91  PNM */
340        static struct beziersegment template =
341                     { BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
342                       NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 } };
343  
344        register struct beziersegment *r;  /* output segment                  */
345  
346        IfTrace3((MustTraceCalls),"..Bezier(%z, %z, %z)\n", B, C, D);
347        ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2,C,D), struct beziersegment *);
348        ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2,B,D), struct beziersegment *);
349        ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2,B,C), struct beziersegment *);
350  
351        r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
352        r->last = (struct segment *) r;
353        r->dest.x = D->dest.x;
354        r->dest.y = D->dest.y;
355        r->B.x = B->dest.x;
356        r->B.y = B->dest.y;
357        r->C.x = C->dest.x;
358        r->C.y = C->dest.y;
359  
360        ConsumePath(B);
361        ConsumePath(C);
362        ConsumePath(D);
363        return(r);
364 }
365  
366 /*
367 :h2.Font "Hint" Segments
368  
369 :h3.Hint() - A Font 'Hint' Segment
370  
371 This is temporary code while we experiment with hints.
372 */
373  
374 /*SHARED LINE(S) ORIGINATED HERE*/
375 struct hintsegment *Hint(S, ref, width, orientation, hinttype, adjusttype, direction, label)
376        struct XYspace *S;
377        float ref;
378        float width;
379        char orientation;
380        char hinttype;
381        char adjusttype;
382        char direction;
383        int label;
384 {
385 /* added reference field of 1 to hintsegment template below 3-26-91 PNM */
386        static struct hintsegment template = { HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
387                                           NULL, NULL, { 0, 0 }, { 0, 0 }, { 0, 0 },
388                                           ' ', ' ', ' ', ' ', 0};
389  
390        register struct hintsegment *r;
391  
392        r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
393  
394        r->orientation = orientation;
395        if (width == 0.0)  width = 1.0;
396  
397        if (orientation == 'h') {
398                (*S->convert)(&r->ref, S, 0.0, ref);
399                (*S->convert)(&r->width, S, 0.0, width);
400        }
401        else if (orientation == 'v') {
402                (*S->convert)(&r->ref, S, ref, 0.0);
403                (*S->convert)(&r->width, S, width, 0.0);
404        }
405        else
406                return((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
407        if (r->width.x < 0)  r->width.x = - r->width.x;
408        if (r->width.y < 0)  r->width.y = - r->width.y;
409        r->hinttype = hinttype;
410        r->adjusttype = adjusttype;
411        r->direction = direction;
412        r->label = label;
413        r->last = (struct segment *) r;
414        ConsumeSpace(S);
415        return(r);
416 }
417  
418 /*
419 */
420  
421 /*SHARED LINE(S) ORIGINATED HERE*/
422  
423 /*
424 POP removes the first segment in a path 'p' and Frees it.  'p' is left
425 pointing to the end of the path:
426 */
427 #define POP(p) \
428      { register struct segment *linkp; \
429        linkp = p->link; \
430        if (linkp != NULL) \
431                linkp->last = p->last; \
432        Free(p); \
433        p = linkp; }
434 /*
435 INSERT inserts a single segment in the middle of a chain.  'b' is
436 the segment before, 'p' the segment to be inserted, and 'a' the
437 segment after.
438 */
439 #define INSERT(b,p,a)  b->link=p; p->link=a; p->last=NULL
440  
441 /*
442 :h3.Join() - Join Two Objects Together
443  
444 If these are paths, this operator simply invokes the CONCAT macro.
445 Why so much code then, you ask?  Well we have to check for object
446 types other than paths, and also check for certain path consistency
447 rules.
448 */
449  
450 struct segment *Join(p1, p2)
451        register struct segment *p1,*p2;
452 {
453        IfTrace2((MustTraceCalls && PathDebug > 1),"..Join(%z, %z)\n", p1, p2);
454        IfTrace2((MustTraceCalls && PathDebug <=1),"..Join(%x, %x)\n", p1, p2);
455 /*
456 We start with a whole bunch of very straightforward argument tests:
457 */
458        if (p2 != NULL) {
459                if (!ISPATHTYPE(p2->type)) {
460  
461                        if (p1 == NULL)
462                                return((struct segment *)Unique(p2));
463  
464                        switch (p1->type) {
465  
466                            case REGIONTYPE:
467  
468                            case STROKEPATHTYPE:
469                                p1 = CoercePath(p1);
470                                break;
471  
472                            default:
473                                return((struct segment *)BegHandle(p1, p2));
474                        }
475                }
476  
477                ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1,p1), struct segment *);
478                p2 = UniquePath(p2);
479  
480 /*
481 In certain circumstances, we don't have to duplicate a permanent
482 location.  (We would just end up destroying it anyway).  These cases
483 are when 'p2' begins with a move-type segment:
484 */
485                if (p2->type == TEXTTYPE || p2->type == MOVETYPE) {
486                        if (p1 == NULL)
487                                return(p2);
488                        if (ISLOCATION(p1)) {
489                                p2->dest.x += p1->dest.x;
490                                p2->dest.y += p1->dest.y;
491                                ConsumePath(p1);
492                                return(p2);
493                        }
494                }
495        }
496        else
497                return((struct segment *)Unique(p1));
498  
499        if (p1 != NULL) {
500                if (!ISPATHTYPE(p1->type))
501  
502                        switch (p2->type) {
503  
504                            case REGIONTYPE:
505  
506                            case STROKEPATHTYPE:
507                                p2 = CoercePath(p2);
508                                break;
509  
510                            default:
511                                return((struct segment *)EndHandle(p1, p2));
512                        }
513  
514                ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1,p2), struct segment *);
515                p1 = UniquePath(p1);
516        }
517        else
518                return(p2);
519  
520 /*
521 At this point all the checking is done.  We have two temporary non-null
522 path types in 'p1' and 'p2'.  If p1 ends with a MOVE, and p2 begins with
523 a MOVE, we collapse the two MOVEs into one.  We enforce the rule that
524 there may not be two MOVEs in a row:
525 */
526  
527        if (p1->last->type == MOVETYPE && p2->type == MOVETYPE) {
528                p1->last->flag |= p2->flag;
529                p1->last->dest.x += p2->dest.x;
530                p1->last->dest.y += p2->dest.y;
531                POP(p2);
532                if (p2 == NULL)
533                        return(p1);
534        }
535 /*
536 Now we check for another silly rule.  If a path has any TEXTTYPEs,
537 then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,
538 it must begin with a TEXTTYPE.  This rule makes it easy to check
539 for the special case of text.  If necessary, we will coerce
540 TEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.
541 */
542        if (p1->type == TEXTTYPE) {
543                if (p2->type != TEXTTYPE && !ISLOCATION(p2))
544                        p1 = CoerceText(p1);
545        }
546        else {
547                if (p2->type == TEXTTYPE) {
548                        if (ISLOCATION(p1)) {
549                                p2->dest.x += p1->dest.x;
550                                p2->dest.y += p1->dest.y;
551                                Free(p1);
552                                return(p2);
553                        }
554                        else
555                                p2 = CoerceText(p2);
556                }
557        }
558 /*
559 Thank God!  Finally!  It's hard to believe, but we are now able to
560 actually do the join.  This is just invoking the CONCAT macro:
561 */
562        CONCAT(p1, p2);
563  
564        return(p1);
565 }
566  
567 /*
568 :h3.JoinSegment() - Create a Path Segment and Join It to a Known Path
569  
570 This internal function is quicker than a full-fledged join because
571 it can do much less checking.
572 */
573  
574 struct segment *t1_JoinSegment(before, type, x, y, after)
575        register struct segment *before;  /* path to join before new segment  */
576        int type;             /* type of new segment (MOVETYPE or LINETYPE)   */
577        fractpel x,y;         /* x,y of new segment                           */
578        register struct segment *after;  /* path to join after new segment    */
579 {
580        register struct segment *r;  /* returned path built here              */
581  
582        r = PathSegment(type, x, y);
583        if (before != NULL) {
584                CONCAT(before, r);
585                r = before;
586        }
587        else
588                r->context = after->context;
589        if (after != NULL)
590                CONCAT(r, after);
591        return(r);
592 }
593  
594 /*
595 :h2.Other Path Functions
596  
597 */
598  
599  
600 struct segment *t1_ClosePath(p0,lastonly)
601        register struct segment *p0;  /* path to close                        */
602        register int lastonly;  /*  flag deciding to close all subpaths or... */
603 {
604        register struct segment *p,*last,*start;  /* used in looping through path */
605        register fractpel x,y;  /* current position in path                   */
606        register fractpel firstx,firsty;  /* start position of sub path       */
607        register struct segment *lastnonhint;  /* last non-hint segment in path */
608  
609        IfTrace1((MustTraceCalls),"ClosePath(%z)\n", p0);
610        if (p0 != NULL && p0->type == TEXTTYPE)
611                return(UniquePath(p0));
612        if (p0->type == STROKEPATHTYPE)
613                return((struct segment *)Unique(p0));
614        /*
615        * NOTE: a null closed path is different from a null open path
616        * and is denoted by a closed (0,0) move segment.  We make
617        * sure this path begins and ends with a MOVETYPE:
618        */
619        if (p0 == NULL || p0->type != MOVETYPE)
620                p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
621        TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0), struct segment *);
622        if (p0->last->type != MOVETYPE)
623                p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
624  
625        p0 = UniquePath(p0);
626  
627 /*
628 We now begin a loop through the path,
629 incrementing current 'x' and 'y'.  We are searching
630 for MOVETYPE segments (breaks in the path) that are not already closed.
631 At each break, we insert a close segment.
632 */
633        for (p = p0, x = y = 0, start = NULL;
634             p != NULL;
635             x += p->dest.x, y += p->dest.y, last = p, p = p->link)
636        {
637  
638                if (p->type == MOVETYPE) {
639                        if (start != NULL && (lastonly?p->link==NULL:TRUE) &&
640                              !(ISCLOSED(start->flag) && LASTCLOSED(last->flag))) {
641                                register struct segment *r;  /* newly created */
642  
643                                start->flag |= ISCLOSED(ON);
644                                r = PathSegment(LINETYPE, firstx - x,
645                                                          firsty - y);
646                                INSERT(last, r, p);
647                                r->flag |= LASTCLOSED(ON);
648                                /*< adjust 'last' if possible for a 0,0 close >*/
649 {
650  
651 #define   CLOSEFUDGE    3    /* if we are this close, let's change last segment */
652  
653        if (r->dest.x != 0 || r->dest.y != 0) {
654                if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
655                     && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE) {
656                        IfTrace2((PathDebug),
657                                "ClosePath forced closed by (%p,%p)\n",
658                                       r->dest.x, r->dest.y);
659                        lastnonhint->dest.x += r->dest.x;
660                        lastnonhint->dest.y += r->dest.y;
661                        r->dest.x = r->dest.y = 0;
662                }
663        }
664 }
665                                if (p->link != NULL) {
666                                        p->dest.x += x - firstx;
667                                        p->dest.y += y - firsty;
668                                        x = firstx;
669                                        y = firsty;
670                                }
671                        }
672                        start = p;
673                        firstx = x + p->dest.x;
674                        firsty = y + p->dest.y;
675                }
676                else if (p->type != HINTTYPE)
677                        lastnonhint = p;
678        }
679        return(p0);
680 }
681 /*
682 */
683 /*
684 :h2.Reversing the Direction of a Path
685  
686 This turned out to be more difficult than I thought at first.  The
687 trickiness was due to the fact that closed paths must remain closed,
688 etc.
689  
690 We need three subroutines:
691 */
692  
693 static struct segment *SplitPath(); /* break a path at any point             */
694 static struct segment *DropSubPath();  /* breaks a path after first sub-path */
695 static struct segment *ReverseSubPath();  /* reverses a single sub-path      */
696  
697 /*
698 :h3.Reverse() - User Operator to Reverse a Path
699  
700 This operator reverses the entire path.
701 */
702  
703 struct segment *Reverse(p)
704        register struct segment *p;    /* full path to reverse                */
705 {
706        register struct segment *r;    /* output path built here              */
707        register struct segment *nextp;  /* contains next sub-path            */
708  
709        IfTrace1((MustTraceCalls),"Reverse(%z)\n", p);
710  
711        if (p == NULL)
712                return(NULL);
713  
714        ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0), struct segment *);
715  
716        if (p->type == TEXTTYPE)
717                p = CoerceText(p);
718        p = UniquePath(p);
719  
720        r = NULL;
721  
722        do {
723                nextp = DropSubPath(p);
724                p = ReverseSubPath(p);
725                r = Join(p, r);
726                p = nextp;
727  
728        } while (p != NULL);
729  
730        return(r);
731 }
732  
733 /*
734 :h4.ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
735 */
736  
737 static struct segment *ReverseSubPath(p)
738        register struct segment *p;  /* input path                            */
739 {
740        register struct segment *r;  /* reversed path will be created here    */
741        register struct segment *nextp;  /* temporary variable used in loop   */
742        register int wasclosed;  /* flag, path was closed                     */
743  
744        if (p == NULL)
745                return(NULL);
746  
747        wasclosed = ISCLOSED(p->flag);
748        r = NULL;
749  
750        do {
751 /*
752 First we reverse the direction of this segment and clean up its flags:
753 */
754                p->dest.x = - p->dest.x;  p->dest.y = - p->dest.y;
755                p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
756  
757                switch (p->type) {
758  
759                    case LINETYPE:
760                    case MOVETYPE:
761                        break;
762  
763                    case CONICTYPE:
764                    {
765 /*
766 The logic of this is that the new M point (stored relative to the new
767 beginning) is (M - C).  However, C ("dest") has already been reversed
768 So, we add "dest" instead of subtracting it:
769 */
770                        register struct conicsegment *cp = (struct conicsegment *) p;
771  
772                        cp->M.x += cp->dest.x;  cp->M.y += cp->dest.y;
773                    }
774                        break;
775  
776                    case BEZIERTYPE:
777                    {
778                        register struct beziersegment *bp = (struct beziersegment *) p;
779  
780                        bp->B.x += bp->dest.x;  bp->B.y += bp->dest.y;
781                        bp->C.x += bp->dest.x;  bp->C.y += bp->dest.y;
782                    }
783                        break;
784  
785                    case HINTTYPE:
786                    {
787                        register struct hintsegment *hp = (struct hintsegment *) p;
788  
789                        hp->ref.x = -hp->ref.x;  hp->ref.y = -hp->ref.y;
790                    }
791                        break;
792  
793                    default:
794                        abort("Reverse: bad path segment");
795                }
796 /*
797 We need to reverse the order of segments too, so we break this segment
798 off of the input path, and tack it on the front of the growing path
799 in 'r':
800 */
801                nextp = p->link;
802                p->link = NULL;
803                p->last = p;
804                if (r != NULL)
805                        CONCAT(p,r);  /* leaves result in 'p'... not what we want */
806                r = p;
807                p = nextp;    /* advance to next segment in input path        */
808  
809        } while (p != NULL);
810  
811        if (wasclosed)
812                r = ClosePath(r);
813  
814        return(r);
815 }
816  
817 /*
818 :h4.DropSubPath() - Drops the First Sub-Path Off a Path
819  
820 This subroutine returns the remaining sub-path(s).  While doing so, it
821 breaks the input path after the first sub-path so that a pointer to
822 the original path now contains the first sub-path only.
823 */
824  
825 static struct segment *DropSubPath(p0)
826        register struct segment *p0;  /* original path                        */
827 {
828        register struct segment *p;  /* returned remainder here               */
829  
830        for (p = p0; p->link != NULL; p = p->link) {
831                if (p->link->type == MOVETYPE)
832                        break;
833        }
834  
835        return(SplitPath(p0, p));
836 }
837  
838 static struct segment *SplitPath(anchor, before)
839        register struct segment *anchor;
840        register struct segment *before;
841 {
842        register struct segment *r;
843  
844        if (before == anchor->last)
845                return(NULL);
846  
847        r = before->link;
848        r->last = anchor->last;
849        anchor->last = before;
850        before->link = NULL;
851  
852        return(r);
853 }
854  
855  
856 /*
857 :h3.ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
858  
859 This user operator reverses the sub-paths in a path, but leaves the
860 'move' segments unchanged.  It builds on top of the subroutines
861 already established.
862 */
863  
864 struct segment *ReverseSubPaths(p)
865        register struct segment *p;  /* input path                            */
866 {
867        register struct segment *r;  /* reversed path will be created here    */
868        register struct segment *nextp;  /* temporary variable used in loop   */
869        int wasclosed;        /* flag; subpath was closed                     */
870        register struct segment *nomove;  /* the part of sub-path without move segment */
871        struct fractpoint delta;
872  
873        IfTrace1((MustTraceCalls),"ReverseSubPaths(%z)\n", p);
874  
875        if (p == NULL)
876                return(NULL);
877  
878        ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0), struct segment *);
879  
880        if (p->type == TEXTTYPE)
881                p = CoerceText(p);
882        if (p->type != MOVETYPE)
883                p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
884  
885        p = UniquePath(p);
886  
887        r = NULL;
888  
889        for (; p != NULL;) {
890                nextp = DropSubPath(p);
891                wasclosed = ISCLOSED(p->flag);
892                if (wasclosed)
893                        UnClose(p);
894  
895                nomove = SplitPath(p, p);
896                r = Join(r, p);
897  
898                PathDelta(nomove, &delta);
899  
900                nomove = ReverseSubPath(nomove);
901                p->dest.x += delta.x;
902                p->dest.y += delta.y;
903                if (nextp != NULL) {
904                        nextp->dest.x += delta.x;
905                        nextp->dest.y += delta.y;
906                }
907                if (wasclosed) {
908                        nomove = ClosePath(nomove);
909                        nextp->dest.x -= delta.x;
910                        nextp->dest.y -= delta.y;
911                }
912                r = Join(r, nomove);
913                p = nextp;
914  
915        }
916  
917        return(r);
918 }
919  
920 static UnClose(p0)
921        register struct segment *p0;
922 {
923        register struct segment *p;
924  
925        for (p=p0; p->link->link != NULL; p=p->link) { ; }
926  
927        if (!LASTCLOSED(p->link->flag))
928                abort("UnClose:  no LASTCLOSED");
929  
930        Free(SplitPath(p0, p));
931        p0->flag &= ~ISCLOSED(ON);
932 }
933  
934 /*
935 :h2.Transforming and Putting Handles on Paths
936  
937 :h3.PathTransform() - Transform a Path
938  
939 Transforming a path involves transforming all the points.  In order
940 that closed paths do not become "unclosed" when their relative
941 positions are slightly changed due to loss of arithmetic precision,
942 all point transformations are in absolute coordinates.
943  
944 (It might be better to reset the "absolute" coordinates every time a
945 move segment is encountered.  This would mean that we could accumulate
946 error from subpath to subpath, but we would be less likely to make
947 the "big error" where our fixed point arithmetic "wraps".  However, I
948 think I'll keep it this way until something happens to convince me
949 otherwise.)
950  
951 The transform is described as a "space", that way we can use our
952 old friend the "iconvert" function, which should be very efficient.
953 */
954  
955 struct segment *PathTransform(p0, S)
956        register struct segment *p0;    /* path to transform                  */
957        register struct XYspace *S;     /* pseudo space to transform in       */
958 {
959        register struct segment *p;   /* to loop through path with            */
960        register fractpel newx,newy;  /* current transformed position in path */
961        register fractpel oldx,oldy;  /* current untransformed position in path */
962        register fractpel savex,savey;  /* save path delta x,y                */
963  
964        p0 = UniquePath(p0);
965  
966        newx = newy = oldx = oldy = 0;
967  
968        for (p=p0; p != NULL; p=p->link) {
969  
970                savex = p->dest.x;   savey = p->dest.y;
971  
972                (*S->iconvert)(&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
973                p->dest.x -= newx;
974                p->dest.y -= newy;
975  
976                switch (p->type) {
977  
978                    case LINETYPE:
979                    case MOVETYPE:
980                        break;
981  
982                    case CONICTYPE:
983                    {
984                        register struct conicsegment *cp = (struct conicsegment *) p;
985  
986                        (*S->iconvert)(&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
987                        cp->M.x -= newx;
988                        cp->M.y -= newy;
989                        /*
990                        * Note roundness doesn't change... linear transform
991                        */
992                        break;
993                    }
994  
995  
996                    case BEZIERTYPE:
997                    {
998                        register struct beziersegment *bp = (struct beziersegment *) p;
999  
1000                        (*S->iconvert)(&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
1001                        bp->B.x -= newx;
1002                        bp->B.y -= newy;
1003                        (*S->iconvert)(&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
1004                        bp->C.x -= newx;
1005                        bp->C.y -= newy;
1006                        break;
1007                    }
1008  
1009                    case HINTTYPE:
1010                    {
1011                        register struct hintsegment *hp = (struct hintsegment *) p;
1012  
1013                        (*S->iconvert)(&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
1014                        hp->ref.x -= newx;
1015                        hp->ref.y -= newy;
1016                        (*S->iconvert)(&hp->width, S, hp->width.x, hp->width.y);
1017                        /* Note: width is not relative to origin */
1018                        break;
1019                    }
1020  
1021                    case TEXTTYPE:
1022                    {
1023                         XformText(p,S);
1024                         break;
1025                    }
1026  
1027                    default:
1028                        IfTrace1(TRUE,"path = %z\n", p);
1029                        abort("PathTransform:  invalid segment");
1030                }
1031                oldx += savex;
1032                oldy += savey;
1033                newx += p->dest.x;
1034                newy += p->dest.y;
1035        }
1036        return(p0);
1037 }
1038  
1039 /*
1040 :h3.PathDelta() - Return a Path's Ending Point
1041 */
1042  
1043 void PathDelta(p, pt)
1044        register struct segment *p; /* input path                             */
1045        register struct fractpoint *pt; /* pointer to x,y to set              */
1046 {
1047        struct fractpoint mypoint;  /* I pass this to TextDelta               */
1048        register fractpel x,y;  /* working variables for path current point   */
1049  
1050        for (x=y=0; p != NULL; p=p->link) {
1051                x += p->dest.x;
1052                y += p->dest.y;
1053                if (p->type == TEXTTYPE) {
1054                        TextDelta(p, &mypoint);
1055                        x += mypoint.x;
1056                        y += mypoint.y;
1057                }
1058        }
1059  
1060        pt->x = x;
1061        pt->y = y;
1062 }
1063  
1064 /*
1065 :h3.BoundingBox() - Produce a Bounding Box Path
1066  
1067 This function is called by image code, when we know the size of the
1068 image in pels, and need to get a bounding box path that surrounds it.
1069 The starting/ending handle is in the lower right hand corner.
1070 */
1071 struct segment *BoundingBox(h, w)
1072        register pel h,w;     /* size of box                                  */
1073 {
1074        register struct segment *path;
1075  
1076        path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
1077        path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
1078        path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
1079        path = ClosePath(path);
1080  
1081        return(path);
1082 }
1083  
1084 /*
1085 :h2.Querying Locations and Paths
1086  
1087 :h3.QueryLoc() - Return the X,Y of a Locition
1088 */
1089  
1090 void QueryLoc(P, S, xP, yP)
1091        register struct segment *P;  /* location to query, not consumed       */
1092        register struct XYspace *S;  /* XY space to return coordinates in     */
1093        register double *xP,*yP;  /* coordinates returned here                */
1094 {
1095        IfTrace4((MustTraceCalls),"QueryLoc(P=%z, S=%z, (%x, %x))\n",
1096                                             P, S, xP, yP);
1097        if (!ISLOCATION(P)) {
1098                ArgErr("QueryLoc: first arg not a location", P, NULL);
1099                return;
1100        }
1101        if (S->type != SPACETYPE) {
1102                ArgErr("QueryLoc: second arg not a space", S, NULL);
1103                return;
1104        }
1105        UnConvert(S, &P->dest, xP, yP);
1106 }
1107 /*
1108 :h3.QueryPath() - Find Out the Type of Segment at the Head of a Path
1109  
1110 This is a very simple routine that looks at the first segment of a
1111 path and tells the caller what it is, as well as returning the control
1112 point(s) of the path segment.  Different path segments have different
1113 number of control points.  If the caller knows that the segment is
1114 a move segment, for example, he only needs to pass pointers to return
1115 one control point.
1116 */
1117  
1118 void QueryPath(path, typeP, Bp, Cp, Dp, fP)
1119        register struct segment *path;  /* path to check                      */
1120        register int *typeP;  /* return the type of path here                 */
1121        register struct segment **Bp;  /* return location of first point      */
1122        register struct segment **Cp;  /* return location of second point     */
1123        register struct segment **Dp;  /* return location of third point      */
1124        register double *fP;  /* return Conic sharpness                       */
1125 {
1126        register int coerced = FALSE;  /* did I coerce a text path?           */
1127  
1128        IfTrace3((MustTraceCalls), "QueryPath(%z, %x, %x, ...)\n",
1129                                              path, typeP, Bp);
1130        if (path == NULL) {
1131                *typeP = -1;
1132                return;
1133        }
1134        if (!ISPATHANCHOR(path)) {
1135                ArgErr("QueryPath: arg not a valid path", path, NULL);
1136        }
1137        if (path->type == TEXTTYPE) {
1138                path = CoerceText(path);
1139                coerced = TRUE;
1140        }
1141  
1142        switch (path->type) {
1143  
1144            case MOVETYPE:
1145                *typeP = 0;
1146                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1147                break;
1148  
1149            case LINETYPE:
1150                *typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
1151                *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
1152                break;
1153  
1154            case CONICTYPE:
1155            {
1156                register struct conicsegment *cp = (struct conicsegment *) path;
1157  
1158                *typeP = 2;
1159                *Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
1160                *Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
1161                *fP = cp->roundness;
1162            }
1163                break;
1164  
1165            case BEZIERTYPE:
1166            {
1167                register struct beziersegment *bp = (struct beziersegment *) path;
1168  
1169                *typeP = 3;
1170                *Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
1171                *Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
1172                *Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
1173            }
1174                break;
1175  
1176            case HINTTYPE:
1177                *typeP = 5;
1178                break;
1179  
1180            default:
1181                abort("QueryPath: unknown segment");
1182        }
1183        if (coerced)
1184                KillPath(path);
1185 }
1186 /*
1187 :h3.QueryBounds() - Return the Bounding Box of a Path
1188  
1189 Returns the bounding box by setting the user's variables.
1190 */
1191  
1192 void QueryBounds(p0, S, xminP, yminP, xmaxP, ymaxP)
1193        register struct segment *p0;  /* object to check for bound            */
1194        struct XYspace *S;    /* coordinate space of returned values          */
1195        double *xminP,*yminP; /* lower left hand corner (set by routine)      */
1196        double *xmaxP,*ymaxP; /* upper right hand corner (set by routine)     */
1197 {
1198        register struct segment *path;  /* loop variable for path segments    */
1199        register fractpel lastx,lasty;  /* loop variables:  previous endingpoint */
1200        register fractpel x,y;  /* loop variables:  current ending point      */
1201        struct fractpoint min;  /* registers to keep lower left hand corner   */
1202        struct fractpoint max;  /* registers to keep upper right hand corner  */
1203        int coerced = FALSE;  /* we have coerced the path from another object */
1204        double x1,y1,x2,y2,x3,y3,x4,y4;  /* corners of rectangle in space X   */
1205  
1206        IfTrace2((MustTraceCalls), "QueryBounds(%z, %z,", p0, S);
1207        IfTrace4((MustTraceCalls), " %x, %x, %x, %x)\n",
1208                                   xminP, yminP, xmaxP, ymaxP);
1209        if (S->type != SPACETYPE) {
1210                ArgErr("QueryBounds:  bad XYspace", S, NULL);
1211                return;
1212        }
1213  
1214        min.x = min.y = max.x = max.y = 0;
1215        if (p0 != NULL) {
1216                if (!ISPATHANCHOR(p0)) {
1217                        switch(p0->type) {
1218                            case STROKEPATHTYPE:
1219       /* replaced DupStrokePath() with Dup() 3-26-91 PNM */
1220                                p0 = (struct segment *) DoStroke(Dup(p0));
1221                                /* no break here, we have a region in p0 */
1222                            case REGIONTYPE:
1223                                p0 = RegionBounds(p0);
1224                                break;
1225  
1226                            case PICTURETYPE:
1227                                p0 = PictureBounds(p0);
1228                                break;
1229  
1230                            default:
1231                                ArgErr("QueryBounds:  bad object", p0, NULL);
1232                                return;
1233                        }
1234                        coerced = TRUE;
1235                }
1236                if (p0->type == TEXTTYPE) {
1237     /* replaced CopyPath() with Dup() 3-26-91 PNM */
1238                        p0 = (struct segment *)CoerceText(Dup(p0));  /* there are faster ways */
1239                        coerced = TRUE;
1240                }
1241                if (p0->type == MOVETYPE) {
1242                        min.x = max.x = p0->dest.x;
1243                        min.y = max.y = p0->dest.y;
1244                }
1245        }
1246        lastx = lasty = 0;
1247  
1248        for (path = p0; path != NULL; path = path->link) {
1249  
1250                x = lastx + path->dest.x;
1251                y = lasty + path->dest.y;
1252  
1253                switch (path->type) {
1254  
1255                    case LINETYPE:
1256                        break;
1257  
1258                    case CONICTYPE:
1259                    {
1260                        register struct conicsegment *cp = (struct conicsegment *) path;
1261                        register fractpel Mx = lastx + cp->M.x;
1262                        register fractpel My = lasty + cp->M.y;
1263                        register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
1264                        register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
1265                        register fractpel Px = Mx - deltax;
1266                        register fractpel Py = My - deltay;
1267                        register fractpel Qx = Mx + deltax;
1268                        register fractpel Qy = My + deltay;
1269  
1270  
1271                        if (Mx < min.x) min.x = Mx;
1272                        else if (Mx > max.x) max.x = Mx;
1273                        if (My < min.y) min.y = My;
1274                        else if (My > max.y) max.y = My;
1275  
1276                        if (Px < min.x) min.x = Px;
1277                        else if (Px > max.x) max.x = Px;
1278                        if (Py < min.y) min.y = Py;
1279                        else if (Py > max.y) max.y = Py;
1280  
1281                        if (Qx < min.x) min.x = Qx;
1282                        else if (Qx > max.x) max.x = Qx;
1283                        if (Qy < min.y) min.y = Qy;
1284                        else if (Qy > max.y) max.y = Qy;
1285                    }
1286                        break;
1287  
1288  
1289                    case MOVETYPE:
1290                        /*
1291                        * We can't risk adding trailing Moves to the
1292                        * bounding box:
1293                        */
1294                        if (path->link == NULL)
1295                                goto done;  /* God forgive me                 */
1296                        break;
1297  
1298                    case BEZIERTYPE:
1299                    {
1300                        register struct beziersegment *bp = (struct beziersegment *) path;
1301                        register fractpel Bx = lastx + bp->B.x;
1302                        register fractpel By = lasty + bp->B.y;
1303                        register fractpel Cx = lastx + bp->C.x;
1304                        register fractpel Cy = lasty + bp->C.y;
1305  
1306                        if (Bx < min.x) min.x = Bx;
1307                        else if (Bx > max.x) max.x = Bx;
1308                        if (By < min.y) min.y = By;
1309                        else if (By > max.y) max.y = By;
1310  
1311                        if (Cx < min.x) min.x = Cx;
1312                        else if (Cx > max.x) max.x = Cx;
1313                        if (Cy < min.y) min.y = Cy;
1314                        else if (Cy > max.y) max.y = Cy;
1315                    }
1316                        break;
1317  
1318                    case HINTTYPE:
1319                        break;
1320                    default:
1321                        abort("QueryBounds: unknown type");
1322                }
1323  
1324                if (x < min.x) min.x = x;
1325                else if (x > max.x) max.x = x;
1326                if (y < min.y) min.y = y;
1327                else if (y > max.y) max.y = y;
1328  
1329                lastx = x;   lasty = y;
1330        }
1331 done:
1332        UnConvert(S, &min, &x1, &y1);
1333        UnConvert(S, &max, &x4, &y4);
1334        x = min.x;  min.x = max.x; max.x = x;
1335        UnConvert(S, &min, &x2, &y2);
1336        UnConvert(S, &max, &x3, &y3);
1337  
1338        *xminP = *xmaxP = x1;
1339        if (x2 < *xminP)  *xminP = x2;
1340        else if (x2 > *xmaxP)  *xmaxP = x2;
1341        if (x3 < *xminP)  *xminP = x3;
1342        else if (x3 > *xmaxP)  *xmaxP = x3;
1343        if (x4 < *xminP)  *xminP = x4;
1344        else if (x4 > *xmaxP)  *xmaxP = x4;
1345  
1346        *yminP = *ymaxP = y1;
1347        if (y2 < *yminP)  *yminP = y2;
1348        else if (y2 > *ymaxP)  *ymaxP = y2;
1349        if (y3 < *yminP)  *yminP = y3;
1350        else if (y3 > *ymaxP)  *ymaxP = y3;
1351        if (y4 < *yminP)  *yminP = y4;
1352        else if (y4 > *ymaxP)  *ymaxP = y4;
1353  
1354        if (coerced)
1355                Destroy(p0);
1356 }
1357 /*
1358 :h3.BoxPath()
1359 */
1360 struct segment *BoxPath(S, h, w)
1361        struct XYspace *S;
1362        int h,w;
1363 {
1364        struct segment *path;
1365  
1366        path = Join( Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)) );
1367        path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
1368        return(ClosePath(path));
1369 }
1370  
1371 /*
1372 :h3.DropSegment() - Drop the First Segment in a Path
1373  
1374 This routine takes the path and returns a new path that is one segment
1375 shorter.  It can be used in conjunction with QueryPath(), for example,
1376 to ask about an entire path.
1377 */
1378  
1379 struct segment *DropSegment(path)
1380        register struct segment *path;
1381 {
1382        IfTrace1((MustTraceCalls),"DropSegment(%z)\n", path);
1383        if (path != NULL && path->type == STROKEPATHTYPE)
1384                path = CoercePath(path);
1385        ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
1386                  "DropSegment: arg not a non-null path", path, path, (0), struct segment *);
1387        if (path->type == TEXTTYPE)
1388                path = CoerceText(path);
1389        path = UniquePath(path);
1390  
1391        POP(path);
1392        return(path);
1393 }
1394 /*
1395 :h3.HeadSegment() - Return the First Segment in a Path
1396  
1397 This routine takes the path and returns a new path consists of the
1398 first segment only.
1399 */
1400  
1401 struct segment *HeadSegment(path)
1402        register struct segment *path;  /* input path                         */
1403 {
1404        IfTrace1((MustTraceCalls),"HeadSegment(%z)\n", path);
1405        if (path == NULL)
1406                return(NULL);
1407        if (path->type == STROKEPATHTYPE)
1408                path = CoercePath(path);
1409        ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0), struct segment *);
1410        if (path->type == TEXTTYPE)
1411                path = CoerceText(path);
1412        path = UniquePath(path);
1413  
1414        if (path->link != NULL)
1415                KillPath(path->link);
1416        path->link = NULL;
1417        path->last = path;
1418        return(path);
1419 }
1420  
1421 /*
1422 :h2.Path Debug Routines
1423  
1424 :h3.DumpPath() - Display a Path on the Trace File
1425 */
1426  
1427 void DumpPath(p)
1428        register struct segment *p;
1429 {
1430        register fractpel x,y;
1431        register fractpel lastx,lasty;
1432        double roundness;
1433  
1434        IfTrace1(TRUE,"Dumping path, anchor=%x:\n", p);
1435        lastx = lasty = 0;
1436  
1437        for (;p != NULL; p=p->link) {
1438  
1439                IfTrace0(TRUE,". ");
1440                x = p->dest.x;
1441                y = p->dest.y;
1442                switch (p->type) {
1443  
1444                    case LINETYPE:
1445                        IfTrace1(TRUE,". line<%x> to", (long) p->flag);
1446                        IfTrace4(TRUE," (%p,%p), delta=(%p,%p)",
1447                                  x + lastx, y + lasty, x, y);
1448                        break;
1449  
1450                    case MOVETYPE:
1451                        IfTrace1(TRUE,"MOVE<%x> to", (long) p->flag);
1452                        IfTrace4(TRUE,"(%p,%p), delta=(%p,%p)",
1453                                  x + lastx, y + lasty, x, y);
1454                        break;
1455  
1456                    case CONICTYPE:
1457                    {
1458                        register struct conicsegment *cp = (struct conicsegment *) p;
1459  
1460                        roundness = cp->roundness;
1461                        IfTrace2(TRUE, ". conic to (%p,%p),",
1462                                                   x + lastx, y + lasty);
1463                        IfTrace3(TRUE," M=(%p,%p), r=%f", cp->M.x + lastx,
1464                                                    cp->M.y + lasty, &roundness);
1465                    }
1466                        break;
1467  
1468                    case BEZIERTYPE:
1469                    {
1470                        register struct beziersegment *bp = (struct beziersegment *) p;
1471  
1472                        IfTrace4(TRUE,". bezier to (%p,%p), B=(%p,%p)",
1473                                        x + lastx, y + lasty,
1474                                        bp->B.x + lastx, bp->B.y + lasty);
1475                        IfTrace2(TRUE, ", C=(%p,%p)",
1476                                        bp->C.x + lastx, bp->C.y + lasty);
1477                    }
1478                        break;
1479  
1480                    case HINTTYPE:
1481                    {
1482                        register struct hintsegment *hp = (struct hintsegment *) p;
1483  
1484                        IfTrace4(TRUE,". hint ref=(%p,%p), width=(%p,%p)",
1485                                        hp->ref.x + lastx, hp->ref.y + lasty,
1486                                        hp->width.x, hp->width.y);
1487                        IfTrace4(TRUE, ", %c %c %c %c",
1488                                        hp->orientation, hp->hinttype,
1489                                        hp->adjusttype, hp->direction);
1490                        IfTrace1(TRUE, ", %ld", (long) hp->label);
1491                    }
1492                        break;
1493  
1494                    case TEXTTYPE:
1495                        DumpText(p);
1496                        break;
1497  
1498                    default:
1499                        IfTrace0(TRUE, "bad path segment?");
1500                }
1501                IfTrace1(TRUE," at %x\n", p);
1502                lastx += x;
1503                lasty += y;
1504        }
1505 }
1506  
1507