]> git.sesse.net Git - rdpsrv/blob - Xserver/lib/font/Type1/type1.c
Support RDP5 logon packets.
[rdpsrv] / Xserver / lib / font / Type1 / type1.c
1 /* $XConsortium: type1.c,v 1.7 94/02/07 15:30:22 gildea Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
3  * All Rights Reserved
4  * Copyright Lexmark International, Inc. 1991
5  * All Rights Reserved
6  * Portions Copyright (c) 1990 Adobe Systems Incorporated.
7  * All Rights Reserved
8  *
9  * License to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation, and that the name of IBM or Lexmark or Adobe
14  * not be used in advertising or publicity pertaining to distribution of
15  * the software without specific, written prior permission.
16  *
17  * IBM, LEXMARK, AND ADOBE PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY
18  * WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT
19  * LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20  * PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE
21  * ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING
22  * ANY DUTY TO SUPPORT OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY
23  * PORTION OF THE SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM,
24  * LEXMARK, OR ADOBE) ASSUMES THE ENTIRE COST OF ALL SERVICING, REPAIR AND
25  * CORRECTION.  IN NO EVENT SHALL IBM, LEXMARK, OR ADOBE BE LIABLE FOR ANY
26  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
27  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
28  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
29  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30  */
31  
32 /*********************************************************************/
33 /*                                                                   */
34 /* Type 1 module - Converting fonts in Adobe Type 1 Font Format      */
35 /*                 to scaled and hinted paths for rasterization.     */
36 /*                 Files: type1.c, type1.h, and blues.h.             */
37 /*                                                                   */
38 /* Authors:   Sten F. Andler, IBM Almaden Research Center            */
39 /*                 (Type 1 interpreter, stem & flex hints)           */
40 /*                                                                   */
41 /*            Patrick A. Casey, Lexmark International, Inc.          */
42 /*                 (Font level hints & stem hints)                   */
43 /*                                                                   */
44 /*********************************************************************/
45  
46 /******************/
47 /* Include Files: */
48 /******************/
49 #include  <stdio.h>          /* a system-dependent include, usually */
50  
51 #include  "objects.h"
52 #include  "spaces.h"
53 #include  "paths.h"
54 #include  "fonts.h"        /* understands about TEXTTYPEs */
55 #include  "pictures.h"     /* understands about handles */
56  
57 typedef struct xobject xobject;
58 #include  "util.h"       /* PostScript objects */
59 #include  "blues.h"          /* Blues structure for font-level hints */
60  
61 /**********************************/
62 /* Type1 Constants and Structures */
63 /**********************************/
64 #define MAXSTACK 24        /* Adobe Type1 limit */
65 #define MAXCALLSTACK 10    /* Adobe Type1 limit */
66 #define MAXPSFAKESTACK 32  /* Max depth of fake PostScript stack (local) */
67 #define MAXSTRLEN 512      /* Max length of a Type 1 string (local) */
68 #define MAXLABEL 256       /* Maximum number of new hints */
69 #define MAXSTEMS 128       /* Maximum number of VSTEM and HSTEM hints */
70 #define EPS 0.001          /* Small number for comparisons */
71  
72 /************************************/
73 /* Adobe Type 1 CharString commands */
74 /************************************/
75 #define HSTEM        1
76 #define VSTEM        3
77 #define VMOVETO      4
78 #define RLINETO      5
79 #define HLINETO      6
80 #define VLINETO      7
81 #define RRCURVETO    8
82 #define CLOSEPATH    9
83 #define CALLSUBR    10
84 #define RETURN      11
85 #define ESCAPE      12
86 #define HSBW        13
87 #define ENDCHAR     14
88 #define RMOVETO     21
89 #define HMOVETO     22
90 #define VHCURVETO   30
91 #define HVCURVETO   31
92  
93 /*******************************************/
94 /* Adobe Type 1 CharString Escape commands */
95 /*******************************************/
96 #define DOTSECTION       0
97 #define VSTEM3           1
98 #define HSTEM3           2
99 #define SEAC             6
100 #define SBW              7
101 #define DIV             12
102 #define CALLOTHERSUBR   16
103 #define POP             17
104 #define SETCURRENTPOINT 33
105  
106 /*****************/
107 /* Useful macros */
108 /*****************/
109 static double tmpx;  /* Store macro argument in tmpx to avoid re-evaluation */
110 static long tmpi;    /* Store converted value in tmpi to avoid re-evaluation */
111  
112 #define FABS(x) (((tmpx = (x)) < 0.0) ? -tmpx : tmpx)
113  
114 #define CEIL(x) (((tmpi = (long) (tmpx = (x))) < tmpx) ? ++tmpi : tmpi)
115  
116 #define FLOOR(x) (((tmpi = (long) (tmpx = (x))) > tmpx) ? --tmpi : tmpi)
117  
118 #define ROUND(x) FLOOR((x) + 0.5)
119  
120 #define ODD(x) (((int)(x)) & 01)
121  
122 #define Error {errflag = TRUE; return;}
123 #define ErrorRet(ret) {errflag = TRUE; return (ret);}
124  
125 #define Error0(errmsg) {IfTrace0(TRUE, errmsg); Error;}
126 #define Error0Ret(errmsg, ret) {IfTrace0(TRUE, errmsg); ErrorRet(ret);}
127  
128 #define Error1(errmsg,arg) {IfTrace1(TRUE, errmsg, arg); Error;}
129  
130 /********************/
131 /* global variables */
132 /********************/
133 struct stem {                     /* representation of a STEM hint */
134     int vertical;                 /* TRUE if vertical, FALSE otherwise */
135     double x, dx;                 /* interval of vertical stem */
136     double y, dy;                 /* interval of horizontal stem */
137     struct segment *lbhint, *lbrevhint;   /* left  or bottom hint adjustment */
138     struct segment *rthint, *rtrevhint;   /* right or top    hint adjustment */
139 };
140  
141 extern struct XYspace *IDENTITY;
142  
143 static double escapementX, escapementY;
144 static double sidebearingX, sidebearingY;
145 static double accentoffsetX, accentoffsetY;
146  
147 static struct segment *path;
148 static int errflag;
149  
150 /*************************************************/
151 /* Global variables to hold Type1Char parameters */
152 /*************************************************/
153 static char *Environment;
154 static struct XYspace *CharSpace;
155 static psobj *CharStringP, *SubrsP, *OtherSubrsP;
156 static int *ModeP;
157  
158 /************************/
159 /* Forward declarations */
160 /************************/
161 static double Div();
162 static double PSFakePop();
163 static DoCommand();
164 static Escape();
165 static HStem();
166 static VStem();
167 static RLineTo();
168 static RRCurveTo();
169 static DoClosePath();
170 static CallSubr();
171 static Return();
172 static EndChar();
173 static RMoveTo();
174 static DotSection();
175 static Seac();
176 static Sbw();
177 static CallOtherSubr();
178 static SetCurrentPoint();
179
180 /*****************************************/
181 /* statics for Flex procedures (FlxProc) */
182 /*****************************************/
183 static struct segment *FlxOldPath; /* save path before Flex feature */
184  
185 /******************************************************/
186 /* statics for Font level hints (Blues) (see blues.h) */
187 /******************************************************/
188 static struct blues_struct *blues; /* the blues structure */
189 static struct alignmentzone alignmentzones[MAXALIGNMENTZONES];
190 static int numalignmentzones;      /* total number of alignment zones */
191  
192 /****************************************************************/
193 /* Subroutines for the Font level hints (Alignment zones, etc.) */
194 /****************************************************************/
195  
196 /******************************************/
197 /* Fill in the alignment zone structures. */
198 /******************************************/
199 static ComputeAlignmentZones()
200 {
201   int i;
202   double dummy, bluezonepixels, familyzonepixels;
203   struct segment *p;
204  
205   numalignmentzones = 0;     /* initialize total # of zones */
206  
207   /* do the BlueValues zones */
208   for (i = 0; i < blues->numBlueValues; i +=2, ++numalignmentzones) {
209     /* the 0th & 1st numbers in BlueValues are for a bottom zone */
210     /* the rest are topzones */
211     if (i == 0)           /* bottom zone */
212       alignmentzones[numalignmentzones].topzone = FALSE;
213     else                  /* top zone */
214       alignmentzones[numalignmentzones].topzone = TRUE;
215     if (i < blues->numFamilyBlues) {    /* we must consider FamilyBlues */
216       p = ILoc(CharSpace,0,blues->BlueValues[i] - blues->BlueValues[i+1]);
217       QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
218       Destroy(p);
219       p = ILoc(CharSpace,0,blues->FamilyBlues[i]-blues->FamilyBlues[i+1]);
220       QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
221       Destroy(p);
222       /* is the difference in size of the zones less than 1 pixel? */
223       if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
224         /* use the Family zones */
225         alignmentzones[numalignmentzones].bottomy =
226           blues->FamilyBlues[i];
227         alignmentzones[numalignmentzones].topy =
228           blues->FamilyBlues[i+1];
229         continue;
230       }
231     }
232     /* use this font's Blue zones */
233     alignmentzones[numalignmentzones].bottomy = blues->BlueValues[i];
234     alignmentzones[numalignmentzones].topy = blues->BlueValues[i+1];
235   }
236  
237   /* do the OtherBlues zones */
238   for (i = 0; i < blues->numOtherBlues; i +=2, ++numalignmentzones) {
239     /* all of the OtherBlues zones are bottom zones */
240     alignmentzones[numalignmentzones].topzone = FALSE;
241     if (i < blues->numFamilyOtherBlues) {/* consider FamilyOtherBlues  */
242       p = ILoc(CharSpace,0,blues->OtherBlues[i] - blues->OtherBlues[i+1]);
243       QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
244       Destroy(p);
245       p = ILoc(CharSpace,0,blues->FamilyOtherBlues[i] -
246         blues->FamilyOtherBlues[i+1]);
247       QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
248       Destroy(p);
249       /* is the difference in size of the zones less than 1 pixel? */
250       if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
251         /* use the Family zones */
252         alignmentzones[numalignmentzones].bottomy =
253           blues->FamilyOtherBlues[i];
254         alignmentzones[numalignmentzones].topy =
255           blues->FamilyOtherBlues[i+1];
256         continue;
257       }
258     }
259     /* use this font's Blue zones (as opposed to the Family Blues */
260     alignmentzones[numalignmentzones].bottomy = blues->OtherBlues[i];
261     alignmentzones[numalignmentzones].topy = blues->OtherBlues[i+1];
262   }
263 }
264  
265 /**********************************************************************/
266 /* Subroutines and statics for handling of the VSTEM and HSTEM hints. */
267 /**********************************************************************/
268 static int InDotSection;             /* DotSection flag */
269 static struct stem stems[MAXSTEMS];  /* All STEM hints */
270 static int numstems;                 /* Number of STEM hints */
271 static int currstartstem;            /* The current starting stem. */
272 static int oldvert, oldhor;          /* Remember hint in effect */
273 static int oldhorhalf, oldverthalf;  /* Remember which half of the stem */
274 static double wsoffsetX, wsoffsetY;  /* White space offset - for VSTEM3,HSTEM3 */
275 static int wsset;                    /* Flag for whether we've set wsoffsetX,Y */
276  
277 static InitStems()  /* Initialize the STEM hint data structures */
278 {
279   InDotSection = FALSE;
280   currstartstem = numstems = 0;
281   oldvert = oldhor = -1;
282 }
283  
284 static FinitStems()  /* Terminate the STEM hint data structures */
285 {
286   int i;
287  
288   for (i = 0; i < numstems; i++) {
289     Destroy(stems[i].lbhint);
290     Destroy(stems[i].lbrevhint);
291     Destroy(stems[i].rthint);
292     Destroy(stems[i].rtrevhint);
293   }
294 }
295  
296 /*******************************************************************/
297 /* Compute the dislocation that a stemhint should cause for points */
298 /* inside the stem.                                                */
299 /*******************************************************************/
300 static ComputeStem(stemno)
301 int stemno;
302 {
303   int verticalondevice, idealwidth;
304   double stemstart, stemwidth;
305   struct segment *p;
306   int i;
307   double stembottom, stemtop, flatposition;
308   double Xpixels, Ypixels;
309   double unitpixels, onepixel;
310   int suppressovershoot, enforceovershoot;
311   double stemshift, flatpospixels, overshoot;
312   double widthdiff; /* Number of character space units to adjust width */
313   double lbhintvalue, rthintvalue;
314   double cxx, cyx, cxy, cyy; /* Transformation matrix */
315   int rotated; /* TRUE if character is on the side, FALSE if upright */
316  
317   /************************************************/
318   /* DETERMINE ORIENTATION OF CHARACTER ON DEVICE */
319   /************************************************/
320  
321   QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
322  
323   if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
324     rotated = TRUE; /* Char is on side (90 or 270 degrees), possibly oblique. */
325   else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
326     rotated = FALSE; /* Char is upright (0 or 180 degrees), possibly oblique. */
327   else {
328     stems[stemno].lbhint = NULL; /* Char is at non-axial angle, ignore hints. */
329     stems[stemno].lbrevhint = NULL;
330     stems[stemno].rthint = NULL;
331     stems[stemno].rtrevhint = NULL;
332     return;
333   }
334  
335   /* Determine orientation of stem */
336  
337   if (stems[stemno].vertical) {
338     verticalondevice = !rotated;
339     stemstart = stems[stemno].x;
340     stemwidth = stems[stemno].dx;
341   } else {
342     verticalondevice = rotated;
343     stemstart = stems[stemno].y;
344     stemwidth = stems[stemno].dy;
345   }
346  
347   /* Determine how many pixels (non-negative) correspond to 1 character space
348      unit (unitpixels), and how many character space units (non-negative)
349      correspond to one pixel (onepixel). */
350  
351   if (stems[stemno].vertical)
352     p = ILoc(CharSpace, 1, 0);
353   else
354     p = ILoc(CharSpace, 0, 1);
355   QueryLoc(p, IDENTITY, &Xpixels, &Ypixels);
356   Destroy(p);
357   if (verticalondevice)
358     unitpixels = FABS(Xpixels);
359   else
360     unitpixels = FABS(Ypixels);
361  
362   onepixel = 1.0 / unitpixels;
363  
364   /**********************/
365   /* ADJUST STEM WIDTHS */
366   /**********************/
367  
368   widthdiff = 0.0;
369  
370   /* Find standard stem with smallest width difference from this stem */
371   if (stems[stemno].vertical) { /* vertical stem */
372     if (blues->StdVW != 0)      /* there is an entry for StdVW */
373       widthdiff = blues->StdVW - stemwidth;
374     for (i = 0; i < blues->numStemSnapV; ++i) { /* now look at StemSnapV */
375       if (blues->StemSnapV[i] - stemwidth < widthdiff)
376         /* this standard width is the best match so far for this stem */
377         widthdiff = blues->StemSnapV[i] - stemwidth;
378     }
379   } else {                      /* horizontal stem */
380     if (blues->StdHW != 0)      /* there is an entry for StdHW */
381       widthdiff = blues->StdHW - stemwidth;
382     for (i = 0; i < blues->numStemSnapH; ++i) { /* now look at StemSnapH */
383       if (blues->StemSnapH[i] - stemwidth < widthdiff)
384         /* this standard width is the best match so far for this stem */
385         widthdiff = blues->StemSnapH[i] - stemwidth;
386     }
387   }
388  
389   /* Only expand or contract stems if they differ by less than 1 pixel from
390      the closest standard width, otherwise make the width difference = 0. */
391   if (FABS(widthdiff) > onepixel)
392     widthdiff = 0.0;
393  
394   /* Expand or contract stem to the nearest integral number of pixels. */
395   idealwidth = ROUND((stemwidth + widthdiff) * unitpixels);
396   /* Ensure that all stems are at least one pixel wide. */
397   if (idealwidth == 0)
398     idealwidth = 1;
399   /* Apply ForceBold to vertical stems. */
400   if (blues->ForceBold && stems[stemno].vertical)
401     /* Force this vertical stem to be at least DEFAULTBOLDSTEMWIDTH wide. */
402     if (idealwidth < DEFAULTBOLDSTEMWIDTH)
403       idealwidth = DEFAULTBOLDSTEMWIDTH;
404   /* Now compute the number of character space units necessary */
405   widthdiff = idealwidth * onepixel - stemwidth;
406  
407   /*********************************************************************/
408   /* ALIGNMENT ZONES AND OVERSHOOT SUPPRESSION - HORIZONTAL STEMS ONLY */
409   /*********************************************************************/
410  
411   stemshift = 0.0;
412  
413   if (!stems[stemno].vertical) {
414  
415     /* Get bottom and top boundaries of the stem. */
416     stembottom = stemstart;
417     stemtop = stemstart + stemwidth;
418  
419     /* Find out if this stem intersects an alignment zone (the BlueFuzz  */
420     /* entry in the Private dictionary specifies the number of character */
421     /* units to extend (in both directions) the effect of an alignment   */
422     /* zone on a horizontal stem.  The default value of BlueFuzz is 1.   */
423     for (i = 0; i < numalignmentzones; ++i) {
424       if (alignmentzones[i].topzone) {
425         if (stemtop >= alignmentzones[i].bottomy &&
426             stemtop <= alignmentzones[i].topy + blues->BlueFuzz) {
427           break; /* We found a top-zone */
428         }
429       } else {
430         if (stembottom <= alignmentzones[i].topy &&
431             stembottom >= alignmentzones[i].bottomy - blues->BlueFuzz) {
432           break; /* We found a bottom-zone */
433         }
434       }
435     }
436  
437     if (i < numalignmentzones) { /* We found an intersecting zone (number i). */
438       suppressovershoot = FALSE;
439       enforceovershoot = FALSE;
440  
441       /* When 1 character space unit is rendered smaller than BlueScale
442          device units (pixels), we must SUPPRESS overshoots.  Otherwise,
443          if the top (or bottom) of this stem is more than BlueShift character
444          space units away from the flat position, we must ENFORCE overshoot. */
445  
446       if (unitpixels < blues->BlueScale)
447         suppressovershoot = TRUE;
448       else
449         if (alignmentzones[i].topzone)
450           if (stemtop >= alignmentzones[i].bottomy + blues->BlueShift)
451             enforceovershoot = TRUE;
452         else
453           if (stembottom <= alignmentzones[i].topy - blues->BlueShift)
454             enforceovershoot = TRUE;
455  
456       /*************************************************/
457       /* ALIGN THE FLAT POSITION OF THE ALIGNMENT ZONE */
458       /*************************************************/
459  
460       /* Compute the position of the alignment zone's flat position in
461          device space and the amount of shift needed to align it on a
462          pixel boundary. Move all stems this amount. */
463  
464       if (alignmentzones[i].topzone)
465         flatposition = alignmentzones[i].bottomy;
466       else
467         flatposition = alignmentzones[i].topy;
468  
469       /* Find the flat position in pixels */
470       flatpospixels = flatposition * unitpixels;
471  
472       /* Find the stem shift necessary to align the flat
473          position on a pixel boundary, and use this shift for all stems */
474       stemshift = (ROUND(flatpospixels) - flatpospixels) * onepixel;
475  
476       /************************************************/
477       /* HANDLE OVERSHOOT ENFORCEMENT AND SUPPRESSION */
478       /************************************************/
479  
480       /* Compute overshoot amount (non-negative) */
481       if (alignmentzones[i].topzone)
482         overshoot = stemtop - flatposition;
483       else
484         overshoot = flatposition - stembottom;
485  
486       if (overshoot > 0.0) {
487         /* ENFORCE overshoot by shifting the entire stem (if necessary) so that
488            it falls at least one pixel beyond the flat position. */
489  
490         if (enforceovershoot)
491           if (overshoot < onepixel)
492             if (alignmentzones[i].topzone)
493               stemshift += onepixel - overshoot;
494             else
495               stemshift -= onepixel - overshoot;
496  
497         /* SUPPRESS overshoot by aligning the stem to the alignment zone's
498            flat position. */
499  
500         if (suppressovershoot)
501           if (alignmentzones[i].topzone)
502             stemshift -= overshoot;
503           else
504             stemshift += overshoot;
505       }
506  
507       /************************************************************/
508       /* COMPUTE HINT VALUES FOR EACH SIDE OF THE HORIZONTAL STEM */
509       /************************************************************/
510  
511       /* If the stem was aligned by a topzone, we expand or contract the stem
512          only at the bottom - since the stem top was aligned by the zone.
513          If the stem was aligned by a bottomzone, we expand or contract the stem
514          only at the top - since the stem bottom was aligned by the zone. */
515       if (alignmentzones[i].topzone) {
516         lbhintvalue = stemshift - widthdiff; /* bottom */
517         rthintvalue = stemshift;             /* top    */
518       } else {
519         lbhintvalue = stemshift;             /* bottom */
520         rthintvalue = stemshift + widthdiff; /* top    */
521       }
522  
523       stems[stemno].lbhint    = (struct segment *)Permanent(Loc(CharSpace, 0.0,  lbhintvalue));
524       stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -lbhintvalue));
525       stems[stemno].rthint    = (struct segment *)Permanent(Loc(CharSpace, 0.0,  rthintvalue));
526       stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -rthintvalue));
527  
528       return;
529  
530     } /* endif (i < numalignmentzones) */
531  
532     /* We didn't find any alignment zones intersecting this stem, so
533        proceed with normal stem alignment below. */
534  
535   } /* endif (!stems[stemno].vertical) */
536  
537   /* Align stem with pixel boundaries on device */
538   stemstart = stemstart - widthdiff / 2;
539   stemshift = ROUND(stemstart * unitpixels) * onepixel - stemstart;
540  
541   /* Adjust the boundaries of the stem */
542   lbhintvalue = stemshift - widthdiff / 2; /* left  or bottom */
543   rthintvalue = stemshift + widthdiff / 2; /* right or top    */
544  
545   if (stems[stemno].vertical) {
546     stems[stemno].lbhint    = (struct segment *)Permanent(Loc(CharSpace,  lbhintvalue, 0.0));
547     stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, -lbhintvalue, 0.0));
548     stems[stemno].rthint    = (struct segment *)Permanent(Loc(CharSpace,  rthintvalue, 0.0));
549     stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, -rthintvalue, 0.0));
550   } else {
551     stems[stemno].lbhint    = (struct segment *)Permanent(Loc(CharSpace, 0.0,  lbhintvalue));
552     stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -lbhintvalue));
553     stems[stemno].rthint    = (struct segment *)Permanent(Loc(CharSpace, 0.0,  rthintvalue));
554     stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -rthintvalue));
555   }
556 }
557  
558 #define LEFT   1
559 #define RIGHT  2
560 #define BOTTOM 3
561 #define TOP    4
562  
563 /*********************************************************************/
564 /* Adjust a point using the given stem hint.  Use the left/bottom    */
565 /* hint value or the right/top hint value depending on where the     */
566 /* point lies in the stem.                                           */
567 /*********************************************************************/
568 static struct segment *Applyhint(p, stemnumber, half)
569 struct segment *p;
570 int stemnumber, half;
571 {
572   if (half == LEFT || half == BOTTOM)
573     return Join(p, stems[stemnumber].lbhint); /* left  or bottom hint */
574   else
575     return Join(p, stems[stemnumber].rthint); /* right or top    hint */
576 }
577  
578 /*********************************************************************/
579 /* Adjust a point using the given reverse hint.  Use the left/bottom */
580 /* hint value or the right/top hint value depending on where the     */
581 /* point lies in the stem.                                           */
582 /*********************************************************************/
583 static struct segment *Applyrevhint(p, stemnumber, half)
584 struct segment *p;
585 int stemnumber, half;
586 {
587   if (half == LEFT || half == BOTTOM)
588     return Join(p, stems[stemnumber].lbrevhint); /* left  or bottom hint */
589   else
590     return Join(p, stems[stemnumber].rtrevhint); /* right or top    hint */
591 }
592  
593 /***********************************************************************/
594 /* Find the vertical and horizontal stems that the current point       */
595 /* (x, y) may be involved in.  At most one horizontal and one vertical */
596 /* stem can apply to a single point, since there are no overlaps       */
597 /* allowed.                                                            */
598 /*   The actual hintvalue is returned as a location.                   */
599 /* Hints are ignored inside a DotSection.                              */
600 /***********************************************************************/
601 static struct segment *FindStems(x, y, dx, dy)
602 double x, y, dx, dy;
603 {
604   int i;
605   int newvert, newhor;
606   struct segment *p;
607   int newhorhalf, newverthalf;
608  
609   if (InDotSection) return(NULL);
610  
611   newvert = newhor = -1;
612   newhorhalf = newverthalf = -1;
613  
614   for (i = currstartstem; i < numstems; i++) {
615     if (stems[i].vertical) { /* VSTEM hint */
616       if ((x >= stems[i].x - EPS) &&
617           (x <= stems[i].x+stems[i].dx + EPS)) {
618         newvert = i;
619         if (dy != 0.0) {
620           if (dy < 0) newverthalf = LEFT;
621           else        newverthalf = RIGHT;
622         } else {
623           if (x < stems[i].x+stems[i].dx / 2) newverthalf = LEFT;
624           else                                newverthalf = RIGHT;
625         }
626       }
627     } else {                 /* HSTEM hint */
628       if ((y >= stems[i].y - EPS) &&
629           (y <= stems[i].y+stems[i].dy + EPS)) {
630         newhor = i;
631         if (dx != 0.0) {
632           if (dx < 0) newhorhalf = TOP;
633           else        newhorhalf = BOTTOM;
634         } else {
635           if (y < stems[i].y+stems[i].dy / 2) newhorhalf = BOTTOM;
636           else                                newhorhalf = TOP;
637         }
638       }
639     }
640   }
641  
642   p = NULL;
643  
644   if (newvert == -1 && oldvert == -1) ; /* Outside of any hints */
645   else if (newvert == oldvert &&
646     newverthalf == oldverthalf); /* No hint change */
647   else if (oldvert == -1) { /* New vertical hint in effect */
648     p = Applyhint(p, newvert, newverthalf);
649   } else if (newvert == -1) { /* Old vertical hint no longer in effect */
650     p = Applyrevhint(p, oldvert, oldverthalf);
651   } else { /* New vertical hint in effect, old hint no longer in effect */
652     p = Applyrevhint(p, oldvert, oldverthalf);
653     p = Applyhint(p, newvert, newverthalf);
654   }
655  
656   if (newhor == -1 && oldhor == -1) ; /* Outside of any hints */
657   else if (newhor == oldhor &&
658     newhorhalf == oldhorhalf) ; /* No hint change */
659   else if (oldhor == -1) { /* New horizontal hint in effect */
660     p = Applyhint(p, newhor, newhorhalf);
661   } else if (newhor == -1) { /* Old horizontal hint no longer in effect */
662     p = Applyrevhint(p, oldhor, oldhorhalf);
663   }
664   else { /* New horizontal hint in effect, old hint no longer in effect */
665     p = Applyrevhint(p, oldhor, oldhorhalf);
666     p = Applyhint(p, newhor, newhorhalf);
667   }
668  
669   oldvert = newvert; oldverthalf = newverthalf;
670   oldhor  = newhor;  oldhorhalf  = newhorhalf;
671  
672   return p;
673 }
674  
675 /******************************************************/
676 /* Subroutines and statics for the Type1Char routines */
677 /******************************************************/
678  
679 static int strindex; /* index into PostScript string being interpreted */
680 static double currx, curry; /* accumulated x and y values for hints */
681  
682 struct callstackentry {
683   psobj *currstrP;        /* current CharStringP */
684   int currindex;          /* current strindex */
685   unsigned short currkey; /* current decryption key */
686   };
687  
688 static double Stack[MAXSTACK];
689 static int Top;
690 static struct callstackentry CallStack[MAXCALLSTACK];
691 static int CallTop;
692 static double PSFakeStack[MAXPSFAKESTACK];
693 static int PSFakeTop;
694  
695 static ClearStack()
696 {
697   Top = -1;
698 }
699  
700 static Push(Num)
701         double Num;
702 {
703   if (++Top < MAXSTACK) Stack[Top] = Num;
704   else Error0("Push: Stack full\n");
705 }
706  
707 static ClearCallStack()
708 {
709   CallTop = -1;
710 }
711  
712 static PushCall(CurrStrP, CurrIndex, CurrKey)
713   psobj *CurrStrP;
714   int CurrIndex;
715   unsigned short CurrKey;
716 {
717   if (++CallTop < MAXCALLSTACK) {
718     CallStack[CallTop].currstrP = CurrStrP;   /* save CharString pointer */
719     CallStack[CallTop].currindex = CurrIndex; /* save CharString index */
720     CallStack[CallTop].currkey = CurrKey;     /* save decryption key */
721   }
722   else Error0("PushCall: Stack full\n");
723 }
724  
725 static PopCall(CurrStrPP, CurrIndexP, CurrKeyP)
726   psobj **CurrStrPP;
727   int *CurrIndexP;
728   unsigned short *CurrKeyP;
729 {
730   if (CallTop >= 0) {
731     *CurrStrPP = CallStack[CallTop].currstrP; /* restore CharString pointer */
732     *CurrIndexP = CallStack[CallTop].currindex; /* restore CharString index */
733     *CurrKeyP = CallStack[CallTop--].currkey;   /* restore decryption key */
734   }
735   else Error0("PopCall: Stack empty\n");
736 }
737  
738 static ClearPSFakeStack()
739 {
740   PSFakeTop = -1;
741 }
742  
743 /* PSFakePush: Pushes a number onto the fake PostScript stack */
744 static PSFakePush(Num)
745   double Num;
746 {
747   if (++PSFakeTop < MAXPSFAKESTACK) PSFakeStack[PSFakeTop] = Num;
748   else Error0("PSFakePush: Stack full\n");
749 }
750  
751 /* PSFakePop: Removes a number from the top of the fake PostScript stack */
752 static double PSFakePop ()
753 {
754   if (PSFakeTop >= 0) return(PSFakeStack[PSFakeTop--]);
755   else Error0Ret("PSFakePop : Stack empty\n", 0.0);
756   /*NOTREACHED*/
757 }
758  
759 /***********************************************************************/
760 /* Center a stem on the pixel grid -- used by HStem3 and VStem3        */
761 /***********************************************************************/
762 static struct segment *CenterStem(edge1, edge2)
763     double edge1;
764     double edge2;
765 {
766   int idealwidth, verticalondevice;
767   double leftx, lefty, rightx, righty, center, width;
768   double widthx, widthy;
769   double shift, shiftx, shifty;
770   double Xpixels, Ypixels;
771   struct segment *p;
772  
773   p = Loc(CharSpace, edge1, 0.0);
774   QueryLoc(p, IDENTITY, &leftx, &lefty);
775  
776   p = Join(p, Loc(CharSpace, edge2, 0.0));
777   QueryLoc(p, IDENTITY, &rightx, &righty);
778   Destroy(p);
779  
780   widthx = FABS(rightx - leftx);
781   widthy = FABS(righty - lefty);
782  
783   if (widthy <= EPS) { /* verticalondevice hint */
784     verticalondevice = TRUE;
785     center = (rightx + leftx) / 2.0;
786     width = widthx;
787   }
788   else if (widthx <= EPS) { /* horizontal hint */
789     verticalondevice = FALSE;
790     center = (righty + lefty) / 2.0;
791     width = widthy;
792   }
793   else { /* neither horizontal nor verticalondevice and not oblique */
794     return (NULL);
795   }
796  
797   idealwidth = ROUND(width);
798   if (idealwidth == 0) idealwidth = 1;
799   if (ODD(idealwidth)) {       /* is ideal width odd? */
800     /* center stem over pixel */
801     shift = FLOOR(center) + 0.5 - center;
802   }
803   else {
804     /* align stem on pixel boundary */
805     shift = ROUND(center) - center;
806   }
807  
808   if (verticalondevice) {
809     shiftx = shift;
810     shifty = 0.0;
811   } else {
812     shifty = shift;
813     shiftx = 0.0;
814   }
815  
816   p = Loc(IDENTITY, shiftx, shifty);
817   QueryLoc(p, CharSpace, &Xpixels, &Ypixels);
818   wsoffsetX = Xpixels; wsoffsetY = Ypixels;
819   currx += wsoffsetX; curry += wsoffsetY;
820  
821   return (p);
822 }
823  
824 /*-----------------------------------------------------------------------
825   Decrypt - From Adobe Type 1 book page 63, with some modifications
826 -----------------------------------------------------------------------*/
827 #define KEY 4330 /* Initial key (seed) for CharStrings decryption */
828 #define C1 52845 /* Multiplier for pseudo-random number generator */
829 #define C2 22719 /* Constant for pseudo-random number generator */
830  
831 static unsigned short r; /* Pseudo-random sequence of keys */
832  
833 static unsigned char Decrypt(cipher)
834 unsigned char cipher;
835 {
836   unsigned char plain;
837  
838   plain = cipher ^ (r >> 8);
839   r = (cipher + r) * C1 + C2;
840   return plain;
841 }
842  
843 /* Get the next byte from the codestring being interpreted */
844 static int DoRead(CodeP)
845   int *CodeP;
846 {
847   if (strindex >= CharStringP->len) return(FALSE); /* end of string */
848   *CodeP = Decrypt((unsigned char) CharStringP->data.stringP[strindex++]);
849   return(TRUE);
850 }
851  
852 /* Strip blues->lenIV bytes from CharString and update encryption key */
853 /* (the lenIV entry in the Private dictionary specifies the number of */
854 /* random bytes at the beginning of each CharString; default is 4)    */
855 static void StartDecrypt()
856 {
857   int Code;
858  
859   r = KEY; /* Initial key (seed) for CharStrings decryption */
860   for (strindex = 0; strindex < blues->lenIV;)
861     if (!DoRead(&Code)) /* Read a byte and update decryption key */
862       Error0("StartDecrypt: Premature end of CharString\n");
863 }
864  
865 static Decode(Code)
866   int Code;
867 {
868   int Code1, Code2, Code3, Code4;
869  
870   if (Code <= 31)                           /* Code is [0,31]    */
871     DoCommand(Code);
872   else if (Code <= 246)                     /* Code is [32,246]  */
873     Push((double)(Code - 139));
874   else if (Code <= 250) {                   /* Code is [247,250] */
875     if (!DoRead(&Code2)) goto ended;
876     Push((double)(((Code - 247) << 8) + Code2 + 108));
877   }
878   else if (Code <= 254) {                   /* Code is [251,254] */
879     if (!DoRead(&Code2)) goto ended;
880     Push((double)( -((Code - 251) << 8) - Code2 - 108));
881   }
882   else {                                    /* Code is 255 */
883     if (!DoRead(&Code1)) goto ended;
884     if (!DoRead(&Code2)) goto ended;
885     if (!DoRead(&Code3)) goto ended;
886     if (!DoRead(&Code4)) goto ended;
887     Push((double)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
888   }
889   return;
890  
891 ended: Error0("Decode: Premature end of Type 1 CharString");
892 }
893  
894 /* Interpret a command code */
895 static DoCommand(Code)
896   int Code;
897 {
898   switch(Code) {
899     case HSTEM: /* |- y dy HSTEM |- */
900       /* Vertical range of a horizontal stem zone */
901       if (Top < 1) Error0("DoCommand: Stack low\n");
902       HStem(Stack[0], Stack[1]);
903       ClearStack();
904       break;
905     case VSTEM: /* |- x dx VSTEM |- */
906       /* Horizontal range of a vertical stem zone */
907       if (Top < 1) Error0("DoCommand: Stack low\n");
908       VStem(Stack[0], Stack[1]);
909       ClearStack();
910       break;
911     case VMOVETO: /* |- dy VMOVETO |- */
912       /* Vertical MOVETO, equivalent to 0 dy RMOVETO */
913       if (Top < 0) Error0("DoCommand: Stack low\n");
914       RMoveTo(0.0, Stack[0]);
915       ClearStack();
916       break;
917     case RLINETO: /* |- dx dy RLINETO |- */
918       /* Like RLINETO in PostScript */
919       if (Top < 1) Error0("DoCommand: Stack low\n");
920       RLineTo(Stack[0], Stack[1]);
921       ClearStack();
922       break;
923     case HLINETO: /* |- dx HLINETO |- */
924       /* Horizontal LINETO, equivalent to dx 0 RLINETO */
925       if (Top < 0) Error0("DoCommand: Stack low\n");
926       RLineTo(Stack[0], 0.0);
927       ClearStack();
928       break;
929     case VLINETO: /* |- dy VLINETO |- */
930       /* Vertical LINETO, equivalent to 0 dy RLINETO */
931       if (Top < 0) Error0("DoCommand: Stack low\n");
932       RLineTo(0.0, Stack[0]);
933       ClearStack();
934       break;
935     case RRCURVETO:
936       /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
937       /* Relative RCURVETO, equivalent to dx1 dy1 */
938       /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
939       /* (dy1+dy2+dy3) RCURVETO in PostScript */
940       if (Top < 5) Error0("DoCommand: Stack low\n");
941       RRCurveTo(Stack[0], Stack[1], Stack[2], Stack[3],
942         Stack[4], Stack[5]);
943       ClearStack();
944       break;
945     case CLOSEPATH: /* - CLOSEPATH |- */
946       /* Closes a subpath without repositioning the */
947       /* current point */
948       DoClosePath();
949       ClearStack();
950       break;
951     case CALLSUBR: /* subr# CALLSUBR - */
952       /* Calls a CharString subroutine with index */
953       /* subr# from the Subrs array */
954       if (Top < 0) Error0("DoCommand: Stack low\n");
955       CallSubr((int)Stack[Top--]);
956       break;
957     case RETURN: /* - RETURN - */
958       /* Returns from a Subrs array CharString */
959       /* subroutine called with CALLSUBR */
960       Return();
961       break;
962     case ESCAPE: /* ESCAPE to two-byte command code */
963       if (!DoRead(&Code)) Error0("DoCommand: ESCAPE is last byte\n");
964       Escape(Code);
965       break;
966     case HSBW: /* |- sbx wx HSBW |- */
967       /* Set the left sidebearing point to (sbx,0), */
968       /* set the character width vector to (wx,0). */
969       /* Equivalent to sbx 0 wx 0 SBW.  Space */
970       /* character should have sbx = 0 */
971       if (Top < 1) Error0("DoCommand: Stack low\n");
972       Sbw(Stack[0], 0.0, Stack[1], 0.0);
973       ClearStack();
974       break;
975     case ENDCHAR: /* - ENDCHAR |- */
976       /* Finishes a CharString outline */
977       EndChar();
978       ClearStack();
979       break;
980     case RMOVETO: /* |- dx dy RMOVETO |- */
981       /* Behaves like RMOVETO in PostScript */
982       if (Top < 1) Error0("DoCommand: Stack low\n");
983       RMoveTo(Stack[0], Stack[1]);
984       ClearStack();
985       break;
986     case HMOVETO: /* |- dx HMOVETO |- */
987       /* Horizontal MOVETO. Equivalent to dx 0 RMOVETO */
988       if (Top < 0) Error0("DoCommand: Stack low\n");
989       RMoveTo(Stack[0], 0.0);
990       ClearStack();
991       break;
992     case VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
993       /* Vertical-Horizontal CURVETO, equivalent to */
994       /* 0 dy1 dx2 dy2 dx3 0 RRCURVETO */
995       if (Top < 3) Error0("DoCommand: Stack low\n");
996       RRCurveTo(0.0, Stack[0], Stack[1], Stack[2],
997               Stack[3], 0.0);
998       ClearStack();
999       break;
1000     case HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
1001       /* Horizontal-Vertical CURVETO, equivalent to */
1002       /* dx1 0 dx2 dy2 0 dy3 RRCURVETO */
1003       if (Top < 3) Error0("DoCommand: Stack low\n");
1004       RRCurveTo(Stack[0], 0.0, Stack[1], Stack[2], 0.0, Stack[3]);
1005       ClearStack();
1006       break;
1007     default: /* Unassigned command code */
1008       ClearStack();
1009       Error1("DoCommand: Unassigned code %d\n", Code);
1010   }
1011 }
1012  
1013 static Escape(Code)
1014   int Code;
1015 {
1016   int i, Num;
1017   struct segment *p;
1018  
1019   switch(Code) {
1020     case DOTSECTION: /* - DOTSECTION |- */
1021       /* Brackets an outline section for the dots in */
1022       /* letters such as "i", "j", and "!". */
1023       DotSection();
1024       ClearStack();
1025       break;
1026     case VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
1027       /* Declares the horizontal ranges of three */
1028       /* vertical stem zones between x0 and x0+dx0, */
1029       /* x1 and x1+dx1, and x2 and x2+dx2. */
1030       if (Top < 5) Error0("DoCommand: Stack low\n");
1031       if (!wsset && ProcessHints) {
1032         /* Shift the whole character so that the middle stem is centered. */
1033         p = CenterStem(Stack[2] + sidebearingX, Stack[3]);
1034         path = Join(path, p);
1035         wsset = 1;
1036       }
1037  
1038       VStem(Stack[0], Stack[1]);
1039       VStem(Stack[2], Stack[3]);
1040       VStem(Stack[4], Stack[5]);
1041       ClearStack();
1042       break;
1043     case HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
1044       /* Declares the vertical ranges of three hori- */
1045       /* zontal stem zones between y0 and y0+dy0, */
1046       /* y1 and y1+dy1, and y2 and y2+dy2. */
1047       if (Top < 5) Error0("DoCommand: Stack low\n");
1048       HStem(Stack[0], Stack[1]);
1049       HStem(Stack[2], Stack[3]);
1050       HStem(Stack[4], Stack[5]);
1051       ClearStack();
1052       break;
1053     case SEAC: /* |- asb adx ady bchar achar SEAC |- */
1054       /* Standard Encoding Accented Character. */
1055       if (Top < 4) Error0("DoCommand: Stack low\n");
1056       Seac(Stack[0], Stack[1], Stack[2],
1057         (unsigned char) Stack[3],
1058         (unsigned char) Stack[4]);
1059       ClearStack();
1060       break;
1061     case SBW: /* |- sbx sby wx wy SBW |- */
1062       /* Set the left sidebearing point to (sbx,sby), */
1063       /* set the character width vector to (wx,wy). */
1064       if (Top < 3) Error0("DoCommand: Stack low\n");
1065       Sbw(Stack[0], Stack[1], Stack[2], Stack[3]);
1066       ClearStack();
1067       break;
1068     case DIV: /* num1 num2 DIV quotient */
1069       /* Behaves like DIV in the PostScript language */
1070       if (Top < 1) Error0("DoCommand: Stack low\n");
1071       Stack[Top-1] = Div(Stack[Top-1], Stack[Top]);
1072       Top--;
1073       break;
1074     case CALLOTHERSUBR:
1075       /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
1076       /* Make calls on the PostScript interpreter */
1077       if (Top < 1) Error0("DoCommand: Stack low\n");
1078       Num = Stack[Top-1];
1079       if (Top < Num+1) Error0("DoCommand: Stack low\n");
1080       for (i = 0; i < Num; i++) PSFakePush(Stack[Top - i - 2]);
1081       Top -= Num + 2;
1082       CallOtherSubr((int)Stack[Top + Num + 2]);
1083       break;
1084     case POP: /* - POP number */
1085       /* Removes a number from the top of the */
1086       /* PostScript interpreter stack and pushes it */
1087       /* onto the Type 1 BuildChar operand stack */
1088       Push(PSFakePop());
1089       break;
1090     case SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
1091       /* Sets the current point to (x,y) in absolute */
1092       /* character space coordinates without per- */
1093       /* forming a CharString MOVETO command */
1094       if (Top < 1) Error0("DoCommand: Stack low\n");
1095       SetCurrentPoint(Stack[0], Stack[1]);
1096       ClearStack();
1097       break;
1098     default: /* Unassigned escape code command */
1099       ClearStack();
1100       Error1("Escape: Unassigned code %d\n", Code);
1101   }
1102 }
1103  
1104 /* |- y dy HSTEM |- */
1105 /* Declares the vertical range of a horizontal stem zone */
1106 /* between coordinates y and y + dy */
1107 /* y is relative to the left sidebearing point */
1108 static HStem(y, dy)
1109   double y, dy;
1110 {
1111   IfTrace2((FontDebug), "Hstem %f %f\n", &y, &dy);
1112   if (ProcessHints) {
1113     if (numstems >= MAXSTEMS) Error0("HStem: Too many hints\n");
1114     if (dy < 0.0) {y += dy; dy = -dy;}
1115     stems[numstems].vertical = FALSE;
1116     stems[numstems].x = 0.0;
1117     stems[numstems].y = sidebearingY + y + wsoffsetY;
1118     stems[numstems].dx = 0.0;
1119     stems[numstems].dy = dy;
1120     ComputeStem(numstems);
1121     numstems++;
1122   }
1123 }
1124  
1125 /* |- x dx VSTEM |- */
1126 /* Declares the horizontal range of a vertical stem zone */
1127 /* between coordinates x and x + dx */
1128 /* x is relative to the left sidebearing point */
1129 static VStem(x, dx)
1130   double x, dx;
1131 {
1132   IfTrace2((FontDebug), "Vstem %f %f\n", &x, &dx);
1133   if (ProcessHints) {
1134     if (numstems >= MAXSTEMS) Error0("VStem: Too many hints\n");
1135     if (dx < 0.0) {x += dx; dx = -dx;}
1136     stems[numstems].vertical = TRUE;
1137     stems[numstems].x = sidebearingX + x + wsoffsetX;
1138     stems[numstems].y = 0.0;
1139     stems[numstems].dx = dx;
1140     stems[numstems].dy = 0.0;
1141     ComputeStem(numstems);
1142     numstems++;
1143   }
1144 }
1145  
1146 /* |- dx dy RLINETO |- */
1147 /* Behaves like RLINETO in PostScript */
1148 static RLineTo(dx, dy)
1149   double dx, dy;
1150 {
1151   struct segment *B;
1152  
1153   IfTrace2((FontDebug), "RLineTo %f %f\n", &dx, &dy);
1154  
1155   B = Loc(CharSpace, dx, dy);
1156  
1157   if (ProcessHints) {
1158     currx += dx;
1159     curry += dy;
1160     /* B = Join(B, FindStems(currx, curry)); */
1161     B = Join(B, FindStems(currx, curry, dx, dy));
1162   }
1163  
1164   path = Join(path, Line(B));
1165 }
1166  
1167 /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
1168 /* Relative RCURVETO, equivalent to dx1 dy1 */
1169 /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
1170 /* (dy1+dy2+dy3) RCURVETO in PostScript */
1171 static RRCurveTo(dx1, dy1, dx2, dy2, dx3, dy3)
1172   double dx1, dy1, dx2, dy2, dx3, dy3;
1173 {
1174   struct segment *B, *C, *D;
1175  
1176   IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ", &dx1, &dy1, &dx2, &dy2);
1177   IfTrace2((FontDebug), "%f %f\n", &dx3, &dy3);
1178  
1179   B = Loc(CharSpace, dx1, dy1);
1180   C = Loc(CharSpace, dx2, dy2);
1181   D = Loc(CharSpace, dx3, dy3);
1182  
1183   if (ProcessHints) {
1184     /* For a Bezier curve, we apply the full hint value to
1185        the Bezier C point (and thereby D point). */
1186     currx += dx1 + dx2 + dx3;
1187     curry += dy1 + dy2 + dy3;
1188     /* C = Join(C, FindStems(currx, curry)); */
1189     C = Join(C, FindStems(currx, curry, dx3, dy3));
1190   }
1191  
1192   /* Since XIMAGER is not completely relative, */
1193   /* we need to add up the delta values */
1194  
1195   C = Join(C, Dup(B));
1196   D = Join(D, Dup(C));
1197  
1198   path = Join(path, Bezier(B, C, D));
1199 }
1200  
1201 /* - CLOSEPATH |- */
1202 /* Closes a subpath WITHOUT repositioning the */
1203 /* current point */
1204 static DoClosePath()
1205 {
1206   struct segment *CurrentPoint;
1207  
1208   IfTrace0((FontDebug), "DoClosePath\n");
1209   CurrentPoint = Phantom(path);
1210   path = ClosePath(path);
1211   path = Join(Snap(path), CurrentPoint);
1212 }
1213  
1214 /* subr# CALLSUBR - */
1215 /* Calls a CharString subroutine with index */
1216 /* subr# from the Subrs array */
1217 static CallSubr(subrno)
1218   int subrno;
1219 {
1220   IfTrace1((FontDebug), "CallSubr %d\n", subrno);
1221   if ((subrno < 0) || (subrno >= SubrsP->len))
1222     Error0("CallSubr: subrno out of range\n");
1223   PushCall(CharStringP, strindex, r);
1224   CharStringP = &SubrsP->data.arrayP[subrno];
1225   StartDecrypt();
1226 }
1227  
1228 /* - RETURN - */
1229 /* Returns from a Subrs array CharString */
1230 /* subroutine called with CALLSUBR */
1231 static Return()
1232 {
1233   IfTrace0((FontDebug), "Return\n");
1234   PopCall(&CharStringP, &strindex, &r);
1235 }
1236  
1237 /* - ENDCHAR |- */
1238 /* Finishes a CharString outline */
1239 /* Executes SETCHACHEDEVICE using a bounding box */
1240 /* it computes directly from the character outline */
1241 /* and using the width information acquired from a previous */
1242 /* HSBW or SBW.  It then calls a special version of FILL */
1243 /* or STROKE depending on the value of PaintType in the */
1244 /* font dictionary */
1245 static EndChar()
1246 {
1247   IfTrace0((FontDebug), "EndChar\n");
1248  
1249   /* There is no need to compute and set bounding box for
1250      the cache, since XIMAGER does that on the fly. */
1251  
1252   /* Perform a Closepath just in case the command was left out */
1253   path = ClosePath(path);
1254  
1255   /* Set character width */
1256   path = Join(Snap(path), Loc(CharSpace, escapementX, escapementY));
1257  
1258 }
1259  
1260 /* |- dx dy RMOVETO |- */
1261 /* Behaves like RMOVETO in PostScript */
1262 static RMoveTo(dx,dy)
1263   double dx,dy;
1264 {
1265   struct segment *B;
1266  
1267   IfTrace2((FontDebug), "RMoveTo %f %f\n", &dx, &dy);
1268  
1269   B = Loc(CharSpace, dx, dy);
1270  
1271   if (ProcessHints) {
1272     currx += dx;
1273     curry += dy;
1274     /* B = Join(B, FindStems(currx, curry)); */
1275     B = Join(B, FindStems(currx, curry, 0.0, 0.0));
1276   }
1277  
1278   path = Join(path, B);
1279 }
1280  
1281 /* - DOTSECTION |- */
1282 /* Brackets an outline section for the dots in */
1283 /* letters such as "i", "j", and "!". */
1284 static DotSection()
1285 {
1286   IfTrace0((FontDebug), "DotSection\n");
1287   InDotSection = !InDotSection;
1288 }
1289  
1290 /* |- asb adx ady bchar achar SEAC |- */
1291 /* Standard Encoding Accented Character. */
1292 static Seac(asb, adx, ady, bchar, achar)
1293   double asb, adx, ady;
1294   unsigned char bchar, achar;
1295 {
1296   int Code;
1297   struct segment *mypath;
1298  
1299   IfTrace4((FontDebug), "SEAC %f %f %f %d ", &asb, &adx, &ady, bchar);
1300   IfTrace1((FontDebug), "%d\n", achar);
1301  
1302   /* Move adx - asb, ady over and up from base char's sbpoint. */
1303   /* (We use adx - asb to counteract the accents sb shift.) */
1304   /* The variables accentoffsetX/Y modify sidebearingX/Y in Sbw(). */
1305   /* Note that these incorporate the base character's sidebearing shift by */
1306   /* using the current sidebearingX, Y values. */
1307   accentoffsetX = sidebearingX + adx - asb;
1308   accentoffsetY = sidebearingY + ady;
1309  
1310   /* Set path = NULL to avoid complaints from Sbw(). */
1311   path = NULL;
1312  
1313   /* Go find the CharString for the accent's code via an upcall */
1314   CharStringP = GetType1CharString(Environment, achar);
1315   StartDecrypt();
1316  
1317   ClearStack();
1318   ClearPSFakeStack();
1319   ClearCallStack();
1320  
1321   for (;;) {
1322     if (!DoRead(&Code)) break;
1323     Decode(Code);
1324     if (errflag) return;
1325   }
1326   /* Copy snapped path to mypath and set path to NULL as above. */
1327   mypath = Snap(path);
1328   path = NULL;
1329  
1330   /* We must reset these to null now. */
1331   accentoffsetX = accentoffsetY = 0;
1332  
1333   /* go find the CharString for the base char's code via an upcall */
1334   CharStringP = GetType1CharString(Environment, bchar);
1335   StartDecrypt();
1336  
1337   ClearStack();
1338   ClearPSFakeStack();
1339   ClearCallStack();
1340  
1341   FinitStems();
1342   InitStems();
1343  
1344   for (;;) {
1345     if (!DoRead(&Code)) break;
1346     Decode(Code);
1347     if (errflag) return;
1348   }
1349   path = Join(mypath, path);
1350 }
1351  
1352  
1353 /* |- sbx sby wx wy SBW |- */
1354 /* Set the left sidebearing point to (sbx,sby), */
1355 /* set the character width vector to (wx,wy). */
1356 static Sbw(sbx, sby, wx, wy)
1357   double sbx, sby, wx, wy;
1358 {
1359   IfTrace4((FontDebug), "SBW %f %f %f %f\n", &sbx, &sby, &wx, &wy);
1360  
1361   escapementX = wx; /* Character width vector */
1362   escapementY = wy;
1363  
1364   /* Sidebearing values are sbx, sby args, plus accent offset from Seac(). */
1365   sidebearingX = sbx + accentoffsetX;
1366   sidebearingY = sby + accentoffsetY;
1367  
1368   path = Join(path, Loc(CharSpace, sidebearingX, sidebearingY));
1369   if (ProcessHints) {currx = sidebearingX; curry = sidebearingY;}
1370 }
1371  
1372  /* num1 num2 DIV quotient */
1373 /* Behaves like DIV in the PostScript language */
1374 static double Div(num1, num2)
1375   double num1, num2;
1376 {
1377   IfTrace2((FontDebug), "Div %f %f\n", &num1, &num2);
1378   return(num1 / num2);
1379 }
1380  
1381 /*
1382   The following four subroutines (FlxProc, FlxProc1, FlxProc2, and
1383   HintReplace) are C versions of the OtherSubrs Programs, which were
1384   were published in the Adobe Type 1 Font Format book.
1385  
1386   The Flex outline fragment is described by
1387     c1: (x0, y0) = c3: (x0, yshrink(y0)) or (xshrink(x0), y0)
1388      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
1389      "  (x2, y2) - reference point
1390     c2: (x0, y0) = c4: (x0, yshrink(y0)) or (xshrink(x0), y0)
1391      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
1392      "  (x2, y2) =  "  (x2, y2), rightmost endpoint
1393     c3: (x0, y0) - control point, 1st Bezier curve
1394      "  (x1, y1) - control point,      -"-
1395      "  (x2, y2) - end point,          -"-
1396     c4: (x0, y0) - control point, 2nd Bezier curve
1397      "  (x1, y1) - control point,      -"-
1398      "  (x2, y2) - end point,          -"-
1399     ep: (epY, epX) - final endpoint (should be same as c4: (x2, y2))
1400     idmin - minimum Flex height (1/100 pixel) at which to render curves
1401 */
1402  
1403 #define dtransform(dxusr,dyusr,dxdev,dydev) { \
1404   register struct segment *point = Loc(CharSpace, dxusr, dyusr); \
1405   QueryLoc(point, IDENTITY, dxdev, dydev); \
1406   Destroy(point); \
1407 }
1408  
1409 #define itransform(xdev,ydev,xusr,yusr) { \
1410   register struct segment *point = Loc(IDENTITY, xdev, ydev); \
1411   QueryLoc(point, CharSpace, xusr, yusr); \
1412   Destroy(point); \
1413 }
1414  
1415 #define transform(xusr,yusr,xdev,ydev) dtransform(xusr,yusr,xdev,ydev)
1416  
1417 #define PaintType (0)
1418  
1419 #define lineto(x,y) { \
1420   struct segment *CurrentPoint; \
1421   double CurrentX, CurrentY; \
1422   CurrentPoint = Phantom(path); \
1423   QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY); \
1424   Destroy(CurrentPoint); \
1425   RLineTo(x - CurrentX, y - CurrentY); \
1426 }
1427  
1428 #define curveto(x0,y0,x1,y1,x2,y2) { \
1429   struct segment *CurrentPoint; \
1430   double CurrentX, CurrentY; \
1431   CurrentPoint = Phantom(path); \
1432   QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY); \
1433   Destroy(CurrentPoint); \
1434   RRCurveTo(x0 - CurrentX, y0 - CurrentY, x1 - x0, y1 - y0, x2 - x1, y2 - y1); \
1435 }
1436  
1437 #define xshrink(x) ((x - c4x2) * shrink +c4x2)
1438 #define yshrink(y) ((y - c4y2) * shrink +c4y2)
1439  
1440 #define PickCoords(flag) \
1441   if (flag) { /* Pick "shrunk" coordinates */ \
1442     x0 = c1x0; y0 = c1y0; \
1443     x1 = c1x1; y1 = c1y1; \
1444     x2 = c1x2; y2 = c1y2; \
1445     x3 = c2x0; y3 = c2y0; \
1446     x4 = c2x1; y4 = c2y1; \
1447     x5 = c2x2; y5 = c2y2; \
1448   } else { /* Pick original coordinates */ \
1449     x0 = c3x0; y0 = c3y0; \
1450     x1 = c3x1; y1 = c3y1; \
1451     x2 = c3x2; y2 = c3y2; \
1452     x3 = c4x0; y3 = c4y0; \
1453     x4 = c4x1; y4 = c4y1; \
1454     x5 = c4x2; y5 = c4y2; \
1455   }
1456  
1457 /* FlxProc() = OtherSubrs[0]; Main part of Flex          */
1458 /*   Calling sequence: 'idmin epX epY 3 0 callothersubr' */
1459 /*   Computes Flex values, and renders the Flex path,    */
1460 /*   and returns (leaves) ending coordinates on stack    */
1461 static void FlxProc(c1x2, c1y2, c3x0, c3y0, c3x1, c3y1, c3x2, c3y2,
1462              c4x0, c4y0, c4x1, c4y1, c4x2, c4y2, epY, epX, idmin)
1463   double c1x2, c1y2;
1464   double c3x0, c3y0, c3x1, c3y1, c3x2, c3y2;
1465   double c4x0, c4y0, c4x1, c4y1, c4x2, c4y2;
1466   double epX, epY;
1467   int idmin;
1468 {
1469   double dmin;
1470   double c1x0, c1y0, c1x1, c1y1;
1471   double c2x0, c2y0, c2x1, c2y1, c2x2, c2y2;
1472   char yflag;
1473   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5;
1474   double cxx, cyx, cxy, cyy; /* Transformation matrix */
1475   int flipXY;
1476   double x, y;
1477   double erosion = 1; /* Device parameter */
1478     /* Erosion may have different value specified in 'internaldict' */
1479   double shrink;
1480   double dX, dY;
1481   char erode;
1482   double eShift;
1483   double cx, cy;
1484   double ex, ey;
1485  
1486   Destroy(path);
1487   path = FlxOldPath; /* Restore previous path (stored in FlxProc1) */
1488  
1489   if (ProcessHints) {
1490     dmin = ABS(idmin) / 100.0; /* Minimum Flex height in pixels */
1491  
1492     c2x2 = c4x2; c2y2 = c4y2; /* Point c2 = c4 */
1493  
1494     yflag = FABS(c1y2 - c3y2) > FABS(c1x2 - c3x2); /* Flex horizontal? */
1495  
1496     QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
1497  
1498     if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
1499       flipXY = -1; /* Char on side */
1500     else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
1501       flipXY = 1; /* Char upright */
1502     else
1503       flipXY = 0; /* Char at angle */
1504  
1505     if (yflag) { /* Flex horizontal */
1506       if (flipXY == 0 || c3y2 == c4y2) { /* Char at angle or Flex height = 0 */
1507         PickCoords(FALSE); /* Pick original control points */
1508       } else {
1509         shrink = FABS((c1y2 - c4y2) / (c3y2 - c4y2)); /* Slope */
1510  
1511         c1x0 = c3x0; c1y0 = yshrink(c3y0);
1512         c1x1 = c3x1; c1y1 = yshrink(c3y1);
1513         c2x0 = c4x0; c2y0 = yshrink(c4y0);
1514         c2x1 = c4x1; c2y1 = yshrink(c4y1);
1515  
1516         dtransform(0.0, ROUND(c3y2-c1y2), &x, &y); /* Flex height in pixels */
1517         dY = FABS((flipXY == 1) ? y : x);
1518         PickCoords(dY < dmin); /* If Flex small, pick 'shrunk' control points */
1519  
1520         if (FABS(y2 - c1y2) > 0.001) { /* Flex 'non-zero'? */
1521           transform(c1x2, c1y2, &x, &y);
1522  
1523           if (flipXY == 1) {
1524             cx = x; cy = y;
1525           } else {
1526             cx = y; cy = x;
1527           }
1528  
1529           dtransform(0.0, ROUND(y2-c1y2), &x, &y);
1530           dY = (flipXY == 1) ? y : x;
1531           if (ROUND(dY) != 0)
1532             dY = ROUND(dY);
1533           else
1534             dY = (dY < 0) ? -1 : 1;
1535  
1536           erode = PaintType != 2 && erosion >= 0.5;
1537           if (erode)
1538             cy -= 0.5;
1539           ey = cy + dY;
1540           ey = CEIL(ey) - ey + FLOOR(ey);
1541           if (erode)
1542             ey += 0.5;
1543  
1544           if (flipXY == 1) {
1545             itransform(cx, ey, &x, &y);
1546           } else {
1547             itransform(ey, cx, &x, &y);
1548           }
1549  
1550           eShift = y - y2;
1551           y1 += eShift;
1552           y2 += eShift;
1553           y3 += eShift;
1554         }
1555       }
1556     } else { /* Flex vertical */
1557       if (flipXY == 0 || c3x2 == c4x2) { /* Char at angle or Flex height = 0 */
1558         PickCoords(FALSE); /* Pick original control points */
1559       } else {
1560         shrink = FABS((c1x2 - c4x2) / (c3x2 - c4x2)); /* Slope */
1561  
1562         c1x0 = xshrink(c3x0); c1y0 = c3y0;
1563         c1x1 = xshrink(c3x1); c1y1 = c3y1;
1564         c2x0 = xshrink(c4x0); c2y0 = c4y0;
1565         c2x1 = xshrink(c4x1); c2y1 = c4y1;
1566  
1567         dtransform(ROUND(c3x2 - c1x2), 0.0, &x, &y); /* Flex height in pixels */
1568         dX = FABS((flipXY == -1) ? y : x);
1569         PickCoords(dX < dmin); /* If Flex small, pick 'shrunk' control points */
1570  
1571         if (FABS(x2 - c1x2) > 0.001) {
1572           transform(c1x2, c1y2, &x, &y);
1573           if (flipXY == -1) {
1574             cx = y; cy = x;
1575           } else {
1576             cx = x; cy = y;
1577           }
1578  
1579           dtransform(ROUND(x2-c1x2), 0.0, &x, &y);
1580           dX = (flipXY == -1) ? y : x;
1581           if (ROUND(dX) != 0)
1582             dX = ROUND(dX);
1583           else
1584             dX = (dX < 0) ? -1 : 1;
1585  
1586           erode = PaintType != 2 && erosion >= 0.5;
1587           if (erode)
1588             cx -= 0.5;
1589           ex = cx + dX;
1590           ex = CEIL(ex) - ex + FLOOR(ex);
1591           if (erode)
1592             ex += 0.5;
1593  
1594           if (flipXY == -1) {
1595             itransform(cy, ex, &x, &y);
1596           } else {
1597             itransform(ex, cy, &x, &y);
1598           }
1599  
1600           eShift = x - x2;
1601           x1 += eShift;
1602           x2 += eShift;
1603           x3 += eShift;
1604         }
1605       }
1606     }
1607  
1608     if (x2 == x5 || y2 == y5) {
1609       lineto(x5, y5);
1610     } else {
1611       curveto(x0, y0, x1, y1, x2, y2);
1612       curveto(x3, y3, x4, y4, x5, y5);
1613     }
1614   } else { /* ProcessHints is off */
1615     PickCoords(FALSE); /* Pick original control points */
1616     curveto(x0, y0, x1, y1, x2, y2);
1617     curveto(x3, y3, x4, y4, x5, y5);
1618   }
1619  
1620   PSFakePush(epY);
1621   PSFakePush(epX);
1622 }
1623  
1624 /* FlxProc1() = OtherSubrs[1]; Part of Flex            */
1625 /*   Calling sequence: '0 1 callothersubr'             */
1626 /*   Saves and clears path, then restores currentpoint */
1627 static void FlxProc1()
1628 {
1629   struct segment *CurrentPoint;
1630  
1631   CurrentPoint = Phantom(path);
1632  
1633   FlxOldPath = path;
1634   path = CurrentPoint;
1635 }
1636  
1637 /* FlxProc2() = OtherSubrs[2]; Part of Flex */
1638 /*   Calling sequence: '0 2 callothersubr'  */
1639 /*   Returns currentpoint on stack          */
1640 static void FlxProc2()
1641 {
1642   struct segment *CurrentPoint;
1643   double CurrentX, CurrentY;
1644  
1645   CurrentPoint = Phantom(path);
1646   QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY);
1647   Destroy(CurrentPoint);
1648  
1649   /* Push CurrentPoint on fake PostScript stack */
1650   PSFakePush(CurrentX);
1651   PSFakePush(CurrentY);
1652 }
1653  
1654 /* HintReplace() = OtherSubrs[3]; Hint Replacement            */
1655 /*   Calling sequence: 'subr# 1 3 callothersubr pop callsubr' */
1656 /*   Reinitializes stem hint structure                        */
1657 static void HintReplace()
1658 {
1659   /* Effectively retire the current stems, but keep them around for */
1660   /* revhint use in case we are in a stem when we replace hints. */
1661   currstartstem = numstems;
1662  
1663   /* 'subr#' is left on PostScript stack (for 'pop callsubr') */
1664 }
1665  
1666 /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
1667 /* Make calls on the PostScript interpreter (or call equivalent C code) */
1668 /* NOTE: The n arguments have been pushed on the fake PostScript stack */
1669 static CallOtherSubr(othersubrno)
1670   int othersubrno;
1671 {
1672   IfTrace1((FontDebug), "CallOtherSubr %d\n", othersubrno);
1673  
1674   switch(othersubrno) {
1675     case 0: /* OtherSubrs[0]; Main part of Flex */
1676       if (PSFakeTop < 16) Error0("CallOtherSubr: PSFakeStack low");
1677       ClearPSFakeStack();
1678       FlxProc(
1679         PSFakeStack[0],  PSFakeStack[1],  PSFakeStack[2],  PSFakeStack[3],
1680         PSFakeStack[4],  PSFakeStack[5],  PSFakeStack[6],  PSFakeStack[7],
1681         PSFakeStack[8],  PSFakeStack[9],  PSFakeStack[10], PSFakeStack[11],
1682         PSFakeStack[12], PSFakeStack[13], PSFakeStack[14], PSFakeStack[15],
1683         (int) PSFakeStack[16]
1684       );
1685       break;
1686     case 1: /* OtherSubrs[1]; Part of Flex */
1687       FlxProc1();
1688       break;
1689     case 2: /* OtherSubrs[2]; Part of Flex */
1690       FlxProc2();
1691       break;
1692     case 3: /* OtherSubrs[3]; Hint Replacement */
1693       HintReplace();
1694       break;
1695     default: { /* call OtherSubrs[4] or higher if PostScript is present */
1696     }
1697   }
1698 }
1699  
1700 /* |- x y SETCURRENTPOINT |- */
1701 /* Sets the current point to (x,y) in absolute */
1702 /* character space coordinates without per- */
1703 /* forming a CharString MOVETO command */
1704 static SetCurrentPoint(x, y)
1705   double x, y;
1706 {
1707   IfTrace2((FontDebug), "SetCurrentPoint %f %f\n", &x, &y);
1708  
1709   currx = x;
1710   curry = y;
1711 }
1712  
1713 /* The Type1Char routine for use by PostScript. */
1714 /************************************************/
1715 struct xobject *Type1Char(env, S, charstrP, subrsP, osubrsP, bluesP, modeP)
1716   char *env;
1717   struct XYspace *S;
1718   psobj *charstrP;
1719   psobj *subrsP;
1720   psobj *osubrsP;
1721   struct blues_struct *bluesP;  /* FontID's ptr to the blues struct */
1722   int *modeP;
1723 {
1724   int Code;
1725  
1726   path = NULL;
1727   errflag = FALSE;
1728  
1729   /* Make parameters available to all Type1 routines */
1730   Environment = env;
1731   CharSpace = S; /* used when creating path elements */
1732   CharStringP = charstrP;
1733   SubrsP = subrsP;
1734   OtherSubrsP = osubrsP;
1735   ModeP = modeP;
1736  
1737     blues = bluesP;
1738  
1739   /* compute the alignment zones */
1740   ComputeAlignmentZones();
1741  
1742   StartDecrypt();
1743  
1744   ClearStack();
1745   ClearPSFakeStack();
1746   ClearCallStack();
1747  
1748   InitStems();
1749  
1750   currx = curry = 0;
1751   escapementX = escapementY = 0;
1752   sidebearingX = sidebearingY = 0;
1753   accentoffsetX = accentoffsetY = 0;
1754   wsoffsetX = wsoffsetY = 0;           /* No shift to preserve whitspace. */
1755   wsset = 0;                           /* wsoffsetX,Y haven't been set yet. */
1756  
1757   for (;;) {
1758     if (!DoRead(&Code)) break;
1759     Decode(Code);
1760     if (errflag) break;
1761   }
1762  
1763   FinitStems();
1764  
1765  
1766   /* Clean up if an error has occurred */
1767   if (errflag) {
1768     if (path != NULL) {
1769       Destroy(path); /* Reclaim storage */
1770       path = NULL;   /* Indicate that character could not be built */
1771     }
1772   }
1773  
1774   return((struct xobject *) path);
1775 }
1776