Back to index

tetex-bin  3.0
type1.c
Go to the documentation of this file.
00001 /* $XConsortium: type1.c,v 1.5 91/10/10 11:20:06 rws Exp $ */
00002 /* Copyright International Business Machines, Corp. 1991
00003  * All Rights Reserved
00004  * Copyright Lexmark International, Inc. 1991
00005  * All Rights Reserved
00006  * Portions Copyright (c) 1990 Adobe Systems Incorporated.
00007  * All Rights Reserved
00008  *
00009  * License to use, copy, modify, and distribute this software and its
00010  * documentation for any purpose and without fee is hereby granted,
00011  * provided that the above copyright notice appear in all copies and that
00012  * both that copyright notice and this permission notice appear in
00013  * supporting documentation, and that the name of IBM or Lexmark or Adobe
00014  * not be used in advertising or publicity pertaining to distribution of
00015  * the software without specific, written prior permission.
00016  *
00017  * IBM, LEXMARK, AND ADOBE PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY
00018  * WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT
00019  * LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
00020  * PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE
00021  * ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING
00022  * ANY DUTY TO SUPPORT OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY
00023  * PORTION OF THE SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM,
00024  * LEXMARK, OR ADOBE) ASSUMES THE ENTIRE COST OF ALL SERVICING, REPAIR AND
00025  * CORRECTION.  IN NO EVENT SHALL IBM, LEXMARK, OR ADOBE BE LIABLE FOR ANY
00026  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
00027  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
00028  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
00029  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00030  */
00031  
00032 /*********************************************************************/
00033 /*                                                                   */
00034 /* Type 1 module - Converting fonts in Adobe Type 1 Font Format      */
00035 /*                 to scaled and hinted paths for rasterization.     */
00036 /*                 Files: type1.c, type1.h, and blues.h.             */
00037 /*                                                                   */
00038 /* Authors:   Sten F. Andler, IBM Almaden Research Center            */
00039 /*                 (Type 1 interpreter, stem & flex hints)           */
00040 /*                                                                   */
00041 /*            Patrick A. Casey, Lexmark International, Inc.          */
00042 /*                 (Font level hints & stem hints)                   */
00043 /*                                                                   */
00044 /*********************************************************************/
00045 
00046 
00047 /* Write debug info into a PostScript file? */
00048 /* #define DUMPDEBUGPATH */
00049 /* If Dumping a debug path, should we dump both character and
00050    outline path? Warning: Do never enable this, unless, your name
00051    is Rainer Menzner and you know what you are doing! */
00052 /* #define DUMPDEBUGPATHBOTH */
00053 
00054 /* Generate a bunch of stderr output to understand and debug
00055    the generation of outline surrounding curves */
00056 /* #define DEBUG_OUTLINE_SURROUNDING */
00057 
00058 #define SUBPATH_CLOSED     1
00059 #define SUBPATH_OPEN       0
00060 
00061 /******************/
00062 /* Include Files: */
00063 /******************/
00064 #include  "types.h"
00065 #include  <stdio.h>          /* a system-dependent include, usually */
00066 #include  <math.h>
00067 #include  <stdlib.h>
00068 
00069 #include  "objects.h"
00070 #include  "spaces.h"
00071 #include  "paths.h"
00072 #include  "fonts.h"        /* understands about TEXTTYPEs */
00073 #include  "pictures.h"     /* understands about handles */
00074  
00075 typedef struct xobject xobject;
00076 #include  "util.h"       /* PostScript objects */
00077 #include  "fontfcn.h"
00078 #include  "blues.h"          /* Blues structure for font-level hints */
00079 
00080 
00081 /* Considerations about hinting (2002-07-11, RMz (Author of t1lib))
00082 
00083    It turns out that the hinting code as used until now produces some
00084    artifacts in which may show up in suboptimal bitmaps. I have therefore
00085    redesigned the algorithm. It is generally a bad idea to hint every
00086    point that falls into a stem hint.
00087 
00088    The idea is to hint only points for
00089    which at least one of the two neighboring curve/line segments is aligned
00090    with the stem in question. For curves, we are speaking about the
00091    tangent line, that is, the line defined by (p1-p2) or (p3-p4).
00092 
00093    For vertical stems this means, that only points which are connected
00094    exactly into vertical direction are hinted. That is, the dx of the
00095    respective curve vanishes. For horizontal stems, accordingly, dy must
00096    vanish at least on one hand side of the point in order to be considered
00097    as a stem.
00098 
00099    Unfortunately this principle requires information about both sides of the
00100    neighborhood of the point in question. In other words, it is not possible
00101    to define a segment completely until the next segment has been inspected.
00102    The idea thus is not compatible with the code in this file.
00103 
00104    Furthermore, if certain points of a character outline are hinted according
00105    to the stem hint info from the charstring, the non-hinted points may not be
00106    left untouched. This would lead to very strong artifacts at small sizes,
00107    especially if characters are defined in terms of curves. This is predominantly
00108    the case for ComputerModern, for example.
00109 
00110    To conclude, it is best to build a point list from the character description
00111    adjust the non-hinted points after hinting has been completely finished. 
00112 
00113    
00114    Another rule we should state is
00115    
00116    We can work around this by not directly connecting the path segments at
00117    the end of the lineto/curveto's, but rather deferring this to the beginning
00118    of the next path constructing function. It's not great but it should work.
00119 
00120    The functions that produce segments are
00121 
00122    1) RMoveTo()
00123    2) RLineto()
00124    3) RRCurveTo()
00125    4) DoClosePath()
00126 
00127    Their code is moved into the switch statement of the new function
00128    handleCurrentSegment(). This function is called when a new segment generating
00129    operation has been decoded from the charstring. At this point a serious
00130    decision about how to hint the points is possible.
00131 
00132    ...
00133 */
00134 
00135 
00136 /* The following struct is used to record points that define a path
00137    in absolute charspace coordinates. x and y describe the location and
00138    hinted, if greater 0, indicates that this point has been hinted. Bit 0
00139    (0x1) indicates vertically adjusted and Bit 1 (0x2) indicates
00140    horizontally adjusted. If hinted == -1, this point is not to be hinted
00141    at all. This, for example, is the case for a a (H)SBW command.
00142 
00143    The member type can be one of
00144 
00145    PPOINT_SBW                --> initial path point as setup by (H)SBW
00146    PPOINT_MOVE               --> point that finishes a MOVE segment
00147    PPOINT_LINE               --> point that finishes a LINE segment
00148    PPOINT_BEZIER_B           --> second point of a BEZIER segment
00149    PPOINT_BEZIER_C           --> third point of a BEZIER segment
00150    PPOINT_BEZIER_D           --> fourth point of a BEZIER segment
00151    PPOINT_CLOSEPATH          --> a ClosePath command
00152    PPOINT_ENDCHAR            --> an EndChar command
00153    PPOINT_SEAC               --> a Standard Encoding Accented Char command
00154    PPOINT_NONE               --> an invalid entry
00155    
00156 
00157    Note: BEZIER_B and BEZIER_C points generally cannot be flagged as
00158    being hinted because are off-curve points.
00159 */
00160 typedef struct 
00161 {
00162   double x;              /* x-coordinate */
00163   double y;              /* y-coordinate */
00164   double ax;             /* adjusted x-coordinate */
00165   double ay;             /* adjusted y-coordinate */
00166   double dxpr;           /* x-shift in right path due to incoming segment (previous) */
00167   double dypr;           /* y-shift in right path due to incoming segment (previous) */
00168   double dxnr;           /* x-shift in right path due to outgoing segment (next) */
00169   double dynr;           /* y-shift in right path due to incoming segment (next) */
00170   double dxir;           /* x-shift in right path resulting from prologation of the linkend tangents (intersect) */
00171   double dyir;           /* y-shift in right path resulting from prologation of the linkend tangents (intersect) */
00172   double dist2prev;      /* distance to the previous point in path (used only for stroking) */
00173   double dist2next;      /* distance to the next point in path (used only for stroking) */
00174   enum 
00175   {
00176     PPOINT_SBW,
00177     PPOINT_MOVE,
00178     PPOINT_LINE,
00179     PPOINT_BEZIER_B,
00180     PPOINT_BEZIER_C,
00181     PPOINT_BEZIER_D,
00182     PPOINT_CLOSEPATH,
00183     PPOINT_ENDCHAR,
00184     PPOINT_SEAC,
00185     PPOINT_NONE
00186   } type;                /* type of path point */
00187   signed char   hinted;  /* is this point hinted? */
00188   unsigned char shape;   /* is the outline concave or convex or straight at this point? This flag
00189                          is only relevant for onCurve points in the context of stroking! */
00190 } PPOINT;
00191 
00192 #define CURVE_NONE            0x00
00193 #define CURVE_STRAIGHT        0x01
00194 #define CURVE_CONVEX          0x02
00195 #define CURVE_CONCAVE         0x03
00196 
00197 #ifdef DEBUG_OUTLINE_SURROUNDING
00198 static char* pptypes[] = {
00199   "PPOINT_SBW",
00200   "PPOINT_MOVE",
00201   "PPOINT_LINE",
00202   "PPOINT_BEZIER_B",
00203   "PPOINT_BEZIER_C",
00204   "PPOINT_BEZIER_D",
00205   "PPOINT_CLOSEPATH",
00206   "PPOINT_ENDCHAR",
00207   "PPOINT_SEAC"
00208 };
00209 static char* ppshapes[] = {
00210   "SHAPE_OFFCURVE",
00211   "SHAPE_STRAIGHT",
00212   "SHAPE_CONVEX",
00213   "SHAPE_CONCAVE"
00214 };
00215 #endif
00216 
00217 
00218 /* The PPOINT structs are organized in an array which is allocated
00219    in chunks of 256 entries. A new point is allocated by a call to
00220    nextPPoint and returns the index in the array of the newly
00221    allocated point. */
00222 static PPOINT* ppoints       = NULL;
00223 static long numppoints       = 0;
00224 static long numppointchunks  = 0;
00225 static int  closepathatfirst = 0;
00226 
00227 static long nextPPoint( void) 
00228 {
00229   ++numppoints;
00230   /* Check whether to reallocate */
00231   if ( numppoints > (numppointchunks * 256) ) {
00232     ++numppointchunks;
00233     ppoints = (PPOINT*) realloc( ppoints, (numppointchunks * 256) * sizeof( PPOINT));
00234   }
00235   /* return the current index */
00236   return numppoints-1;
00237 }
00238 
00239 static void createFillPath( void);
00240 static void createStrokePath( double strokewidth, int subpathclosed);
00241 static void createClosedStrokeSubPath( long startind, long stopind,
00242                                    double strokewidth, int subpathclosed);
00243 static long computeDistances( long startind, long stopind, int subpathclosed);
00244 static void transformOnCurvePathPoint( double strokewidth,
00245                                    long prevind, long currind, long lastind);
00246 static void transformOffCurvePathPoint( double strokewidth, long currind);
00247 /* values for flag:
00248    INTERSECT_PREVIOUS:     only take previous path segment into account.
00249    INTERSECT_NEXT:         only take next path segment into account. 
00250    INTERSECT_BOTH:         do a real intersection
00251 */
00252 #define INTERSECT_PREVIOUS    -1
00253 #define INTERSECT_NEXT         1
00254 #define INTERSECT_BOTH         0
00255 static void intersectRight( long index, double halfwidth, long flag);
00256 /* values for orientation:
00257    PATH_LEFT:              we are constructing the left path.
00258    PATH_RIGHT:             we are constructing the right path.
00259 */
00260 #define PATH_LEFT              1
00261 #define PATH_RIGHT             0
00262 /* values for position:
00263    PATH_START:             current point starts the current path (use next-values).
00264    PATH_END:               current point ends the current path (use prev-values).
00265 */
00266 #define PATH_START             0
00267 #define PATH_END               1
00268 static void linkNode( long index, int position, int orientation);
00269 
00270 
00271 static long handleNonSubPathSegments( long pindex);
00272 static void handleCurrentSegment( long pindex);
00273 static void adjustBezier( long pindex);
00274 
00275 static double  size;
00276 static double scxx, scyx, scxy, scyy;
00277 static double  up;
00278 
00279 #ifdef DUMPDEBUGPATH
00280 static FILE*   psfile = NULL;
00281 static void PSDumpProlog( FILE* fp);
00282 static void PSDumpEpilog( FILE* fp);
00283 #endif
00284 
00285 /**********************************/
00286 /* Type1 Constants and Structures */
00287 /**********************************/
00288 #define MAXSTACK 24        /* Adobe Type1 limit */
00289 #define MAXCALLSTACK 10    /* Adobe Type1 limit */
00290 #define MAXPSFAKESTACK 32  /* Max depth of fake PostScript stack (local) */
00291 #define MAXSTRLEN 512      /* Max length of a Type 1 string (local) */
00292 #define MAXLABEL 256       /* Maximum number of new hints */
00293 #define MAXSTEMS 512       /* Maximum number of VSTEM and HSTEM hints */
00294 #define EPS 0.001          /* Small number for comparisons */
00295 
00296 /************************************/
00297 /* Adobe Type 1 CharString commands */
00298 /************************************/
00299 #define HSTEM        1
00300 #define VSTEM        3
00301 #define VMOVETO      4
00302 #define RLINETO      5
00303 #define HLINETO      6
00304 #define VLINETO      7
00305 #define RRCURVETO    8
00306 #define CLOSEPATH    9
00307 #define CALLSUBR    10
00308 #define RETURN      11
00309 #define ESCAPE      12
00310 #define HSBW        13
00311 #define ENDCHAR     14
00312 #define RMOVETO     21
00313 #define HMOVETO     22
00314 #define VHCURVETO   30
00315 #define HVCURVETO   31
00316 
00317 /* The following charstring code appears in some old Adobe font files
00318    in space and .notdef character and does not seems to do anything
00319    useful aside from taking two args from the stack. We allow this
00320    command and ignore it. The source code of ghostscript states that
00321    this command is obsolete *and* undocumented.
00322    This code may also appear in an Escape-sequence! */
00323 #define UNKNOWN_15  15
00324 
00325 /*******************************************/
00326 /* Adobe Type 1 CharString Escape commands */
00327 /*******************************************/
00328 #define DOTSECTION       0
00329 #define VSTEM3           1
00330 #define HSTEM3           2
00331 #define SEAC             6
00332 #define SBW              7
00333 #define DIV             12
00334 #define CALLOTHERSUBR   16
00335 #define POP             17
00336 #define SETCURRENTPOINT 33
00337  
00338 
00339 /* Note: We use routines from libm because in the original macro definitions,
00340    the evaluation order of tmpx is undefined by C-standard! */
00341 #define USE_MATHLIB_ROUTINES
00342 
00343 #ifdef USE_MATHLIB_ROUTINES
00344 
00345 #define FABS(x) (fabs (x))
00346 #define CEIL(x) ((LONG) ceil (x))
00347 #define FLOOR(x) ((LONG) floor (x))
00348 
00349 #else
00350 
00351 /*****************/
00352 /* Useful macros */
00353 /*****************/
00354 static DOUBLE tmpx;  /* Store macro argument in tmpx to avoid re-evaluation */
00355 static LONG tmpi;    /* Store converted value in tmpi to avoid re-evaluation */
00356 #define FABS(x) (((tmpx = (x)) < 0.0) ? -tmpx : tmpx)
00357 #define CEIL(x) (((tmpi = (LONG) (tmpx = (x))) < tmpx) ? ++tmpi : tmpi)
00358 #define FLOOR(x) (((tmpi = (LONG) (tmpx = (x))) > tmpx) ? --tmpi : tmpi)
00359 
00360 #endif
00361 
00362 #define ROUND(x) FLOOR((x) + 0.5)
00363 #define ODD(x) (((int)(x)) & 01)
00364 
00365 #define CC IfTrace1(TRUE, "Char \"%s\": ", currentchar)
00366 
00367 /* To make some compiler happy we have to care about return types! */
00368 #define Errori {errflag = TRUE; return 0;}    /* integer */
00369 #define Errord {errflag = TRUE; return 0.0;}  /* double */
00370 #define Errorv {errflag = TRUE; return;}      /* void */
00371  
00372 #define Error0i(errmsg) { CC; IfTrace0(TRUE, errmsg); Errori;}
00373 #define Error0d(errmsg) { CC; IfTrace0(TRUE, errmsg); Errord;}
00374 #define Error0v(errmsg) { CC; IfTrace0(TRUE, errmsg); Errorv;}
00375  
00376 #define Error1i(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errori;}
00377 #define Error1d(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errord;}
00378 #define Error1v(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errorv;}
00379  
00380 /********************/
00381 /* global variables */
00382 /********************/
00383 struct stem {              /* representation of a STEM hint */
00384   int vertical;                 /* TRUE if vertical, FALSE otherwise */
00385   DOUBLE x, dx;                 /* interval of vertical stem */
00386   DOUBLE y, dy;                 /* interval of horizontal stem */
00387   DOUBLE alx, aldx;             /* interval of grid-aligned vertical stem */
00388   DOUBLE aly, aldy;             /* interval of grid-aligned horizontal stem */
00389   double lbhintval;             /* adjustment value for left or bottom hint */
00390   double rthintval;             /* adjustment value for right ir top hint */
00391 };
00392  
00393 /******************************************************/
00394 /* Subroutines and statics for the Type1Char routines */
00395 /******************************************************/
00396  
00397 static int strindex; /* index into PostScript string being interpreted */
00398 static double currx, curry;           /* accumulated x and y values */
00399 static double hcurrx, hcurry;         /* accumulated values with hinting */
00400 
00401 
00402 struct callstackentry {
00403   psobj *currstrP;        /* current CharStringP */
00404   int currindex;          /* current strindex */
00405   unsigned short currkey; /* current decryption key */
00406   };
00407  
00408 static DOUBLE Stack[MAXSTACK];
00409 static int Top;
00410 static struct callstackentry CallStack[MAXCALLSTACK];
00411 static int CallTop;
00412 static DOUBLE PSFakeStack[MAXPSFAKESTACK];
00413 static int PSFakeTop;
00414  
00415 
00416 extern struct XYspace *IDENTITY;
00417  
00418 static DOUBLE escapementX, escapementY;
00419 static DOUBLE sidebearingX, sidebearingY;
00420 static DOUBLE accentoffsetX, accentoffsetY;
00421  
00422 static struct segment *path;    /* path of basechar */
00423 static struct segment *apath;   /* pass of accent char */
00424 static int errflag;
00425  
00426 /*************************************************/
00427 /* Global variables to hold Type1Char parameters */
00428 /*************************************************/
00429 static char *Environment;
00430 static char *currentchar;
00431 static struct XYspace *CharSpace;
00432 static psobj *CharStringP, *SubrsP, *OtherSubrsP;
00433 static int *ModeP;
00434  
00435 /************************/
00436 /* Forward declarations */
00437 /************************/
00438 static DOUBLE Div();
00439 static DOUBLE PSFakePop();
00440 static int DoCommand();
00441 static int Escape();
00442 static int HStem();
00443 static int VStem();
00444 static int RLineTo();
00445 static int RRCurveTo();
00446 static int DoClosePath();
00447 static int CallSubr();
00448 static int Return();
00449 static int EndChar();
00450 static int RMoveTo();
00451 static int DotSection();
00452 static int Seac();
00453 static int Sbw();
00454 static int CallOtherSubr();
00455 static int SetCurrentPoint();
00456 
00457 /*****************************************/
00458 /* statics for Flex procedures (FlxProc) */
00459 /*****************************************/
00460 static struct segment *FlxOldPath; /* save path before Flex feature */
00461  
00462 /******************************************************/
00463 /* statics for Font level hints (Blues) (see blues.h) */
00464 /******************************************************/
00465 static struct blues_struct *blues; /* the blues structure */
00466 static struct alignmentzone alignmentzones[MAXALIGNMENTZONES];
00467 int numalignmentzones;          /* total number of alignment zones */
00468  
00469 /****************************************************************/
00470 /* Subroutines for the Font level hints (Alignment zones, etc.) */
00471 /****************************************************************/
00472 
00473 
00474 
00475 /* Flags to control the rasterizer */
00476 #define T1_IGNORE_FORCEBOLD           0x0001
00477 #define T1_IGNORE_FAMILYALIGNMENT     0x0002
00478 #define T1_IGNORE_HINTING             0x0004
00479 
00480 #define T1_DEBUG_LINE                 0x0100
00481 #define T1_DEBUG_REGION               0x0200
00482 #define T1_DEBUG_PATH                 0x0400
00483 #define T1_DEBUG_FONT                 0x0800
00484 #define T1_DEBUG_HINT                 0x1000
00485 
00486 int T1_Type1OperatorFlags; /* for manipulation from t1lib */
00487 
00488 
00489 static void SetRasterFlags( void)
00490 {
00491 
00492   if (T1_Type1OperatorFlags & T1_IGNORE_HINTING)
00493     ProcessHints=0;
00494   else
00495     ProcessHints=1;
00496   
00497   if ( T1_Type1OperatorFlags & T1_DEBUG_LINE)
00498     LineDebug=1;
00499   else
00500     LineDebug=0;
00501   if ( T1_Type1OperatorFlags & T1_DEBUG_REGION)
00502     RegionDebug=1;
00503   else
00504     RegionDebug=0;
00505   if ( T1_Type1OperatorFlags & T1_DEBUG_PATH)
00506     PathDebug=1;
00507   else
00508     PathDebug=0;
00509   if ( T1_Type1OperatorFlags & T1_DEBUG_FONT)
00510     FontDebug=1;
00511   else
00512     FontDebug=0;
00513   if ( T1_Type1OperatorFlags & T1_DEBUG_HINT)
00514     HintDebug=1;
00515   else
00516     HintDebug=0;
00517   return;
00518   
00519 }
00520 
00521 
00522 /******************************************/
00523 /* Fill in the alignment zone structures. */
00524 /******************************************/
00525 static int ComputeAlignmentZones()
00526 {
00527   int i;
00528   DOUBLE dummy, bluezonepixels, familyzonepixels;
00529   struct segment *p;
00530  
00531   numalignmentzones = 0;     /* initialize total # of zones */
00532 
00533   /* Remarks by RMz (Author of t1lib): The handling of substitution of
00534      the BlueValues by the FamilyBlues and correspondingly for the
00535      OtherBlues and FamilyOtherBlues is not clearly documented.
00536      These are the problems:
00537   
00538      1) Does the number of FamilyBlues entries need to be identical to
00539         that of BlueValues?
00540 
00541      2) Obviously, the order of the alignment zones in the BlueValues
00542         and the FamilyBlues need not be same (see TimesBold.pfa)
00543 
00544      3) Is it wise/recommended to make the substitution on a per
00545         alignment-zone level or global, i.e., if once then for all
00546        zones?
00547 
00548      4) The principle found below, checking the delta-height of an
00549         alignment-zone and making a decision based on this is incorrect.
00550        The decision has to be done according to absolute pixel values
00551        at which a feature would be rendered with the BlueValues and the
00552        FamilyBlues respectively.
00553 
00554      To conclude, it seems better to disable the Family-feature until
00555      these things are well-known/defined.
00556      */
00557   
00558   /* do the BlueValues zones */
00559   for (i = 0; i < blues->numBlueValues; i +=2, ++numalignmentzones) {
00560     /* the 0th & 1st numbers in BlueValues are for a bottom zone */
00561     /* the rest are topzones */
00562     if (i == 0)           /* bottom zone */
00563       alignmentzones[numalignmentzones].topzone = FALSE;
00564     else                  /* top zone */
00565       alignmentzones[numalignmentzones].topzone = TRUE;
00566     /* Check FamilyAlignment suppression */
00567     if ( (T1_Type1OperatorFlags & T1_IGNORE_FAMILYALIGNMENT)==0) {
00568       if (i < blues->numFamilyBlues) {    /* we must consider FamilyBlues */
00569        p = ILoc(CharSpace,0,blues->BlueValues[i] - blues->BlueValues[i+1]);
00570        QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
00571        Destroy(p);
00572        p = ILoc(CharSpace,0,blues->FamilyBlues[i] - blues->FamilyBlues[i+1]);
00573        QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
00574        Destroy(p);
00575        /* is the difference in size of the zones less than 1 pixel? */
00576        if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
00577          /* use the Family zones */
00578          alignmentzones[numalignmentzones].bottomy =
00579            blues->FamilyBlues[i];
00580          alignmentzones[numalignmentzones].topy =
00581            blues->FamilyBlues[i+1];
00582 #ifdef DUMPDEBUGPATH
00583          if ( psfile != NULL ) {
00584            if ( alignmentzones[numalignmentzones].topzone == TRUE )
00585              fprintf( psfile, "%f %f t1topzone\n", (blues->FamilyBlues[i])*up,
00586                      (blues->BlueValues[i+1])*up);
00587            else
00588              fprintf( psfile, "%f %f t1bottomzone\n", (blues->FamilyBlues[i])*up,
00589                      (blues->BlueValues[i+1])*up);
00590          }
00591 #endif
00592          continue;
00593        }
00594       }
00595     }
00596     /* use this font's Blue zones */
00597     alignmentzones[numalignmentzones].bottomy = blues->BlueValues[i];
00598     alignmentzones[numalignmentzones].topy = blues->BlueValues[i+1];
00599 #ifdef DUMPDEBUGPATH
00600     if ( psfile != NULL ) {
00601       if ( alignmentzones[numalignmentzones].topzone == TRUE )
00602        fprintf( psfile, "%f %f t1topzone\n", (blues->BlueValues[i])*up,
00603                (blues->BlueValues[i+1])*up);
00604       else
00605        fprintf( psfile, "%f %f t1bottomzone\n", (blues->BlueValues[i])*up,
00606                (blues->BlueValues[i+1])*up);
00607     }
00608 #endif
00609   }
00610  
00611   /* do the OtherBlues zones */
00612   for (i = 0; i < blues->numOtherBlues; i +=2, ++numalignmentzones) {
00613     /* all of the OtherBlues zones are bottom zones */
00614     alignmentzones[numalignmentzones].topzone = FALSE;
00615     /* Check FamilyAlignment suppression */
00616     if ( (T1_Type1OperatorFlags & T1_IGNORE_FAMILYALIGNMENT)==0) {
00617       if (i < blues->numFamilyOtherBlues) {/* consider FamilyOtherBlues  */
00618        p = ILoc(CharSpace,0,blues->OtherBlues[i] - blues->OtherBlues[i+1]);
00619        QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
00620        Destroy(p);
00621        p = ILoc(CharSpace,0,blues->FamilyOtherBlues[i] -
00622                blues->FamilyOtherBlues[i+1]);
00623        QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
00624        Destroy(p);
00625        /* is the difference in size of the zones less than 1 pixel? */
00626        if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
00627          /* use the Family zones */
00628          alignmentzones[numalignmentzones].bottomy =
00629            blues->FamilyOtherBlues[i];
00630          alignmentzones[numalignmentzones].topy =
00631            blues->FamilyOtherBlues[i+1];
00632 #ifdef DUMPDEBUGPATH
00633          if ( psfile != NULL ) {
00634            fprintf( psfile, "%f %f t1bottomzone\n", (blues->FamilyOtherBlues[i])*up,
00635                    (blues->FamilyOtherBlues[i+1])*up);
00636          }
00637 #endif
00638          continue;
00639        }
00640       }
00641     }
00642     /* use this font's Blue zones (as opposed to the Family Blues */
00643     alignmentzones[numalignmentzones].bottomy = blues->OtherBlues[i];
00644     alignmentzones[numalignmentzones].topy = blues->OtherBlues[i+1];
00645 #ifdef DUMPDEBUGPATH
00646     if ( psfile != NULL ) {
00647       fprintf( psfile, "%f %f t1bottomzone\n", (blues->OtherBlues[i])*up,
00648               (blues->OtherBlues[i+1])*up);
00649     }
00650 #endif
00651   }
00652   return(0);
00653   
00654 }
00655  
00656 /**********************************************************************/
00657 /* Subroutines and statics for handling of the VSTEM and HSTEM hints. */
00658 /**********************************************************************/
00659 int InDotSection;             /* DotSection flag */
00660 struct stem stems[MAXSTEMS];  /* All STEM hints */
00661 int numstems;                 /* Number of STEM hints */
00662 int currstartstem;            /* The current starting stem. */
00663 int oldvert, oldhor;          /* Remember hint in effect */
00664 int oldhorhalf, oldverthalf;  /* Remember which half of the stem */
00665 DOUBLE wsoffsetX, wsoffsetY;  /* White space offset - for VSTEM3,HSTEM3 */
00666 int wsset;                    /* Flag for whether we've set wsoffsetX,Y */
00667  
00668 static int InitStems()  /* Initialize the STEM hint data structures */
00669 {
00670   InDotSection = FALSE;
00671   currstartstem = numstems = 0;
00672   oldvert = oldhor = -1;
00673   return(0);
00674   
00675 }
00676  
00677 
00678 /*******************************************************************/
00679 /* Compute the dislocation that a stemhint should cause for points */
00680 /* inside the stem.                                                */
00681 /*******************************************************************/
00682 static int ComputeStem(stemno)
00683 int stemno;
00684 {
00685   int verticalondevice, idealwidth;
00686   DOUBLE stemstart, stemwidth;
00687   struct segment *p;
00688   int i;
00689   DOUBLE stembottom, stemtop, flatposition;
00690   DOUBLE Xpixels, Ypixels;
00691   DOUBLE unitpixels, onepixel;
00692   int suppressovershoot, enforceovershoot;
00693   DOUBLE stemshift, flatpospixels, overshoot;
00694   DOUBLE widthdiff; /* Number of character space units to adjust width */
00695   DOUBLE lbhintvalue, rthintvalue;
00696   DOUBLE cxx, cyx, cxy, cyy; /* Transformation matrix */
00697   int rotated; /* TRUE if character is on the side, FALSE if upright */
00698  
00699   /************************************************/
00700   /* DETERMINE ORIENTATION OF CHARACTER ON DEVICE */
00701   /************************************************/
00702  
00703   QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
00704  
00705   if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
00706     rotated = TRUE; /* Char is on side (90 or 270 degrees), possibly oblique. */
00707   else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
00708     rotated = FALSE; /* Char is upright (0 or 180 degrees), possibly oblique. */
00709   else {
00710     stems[stemno].lbhintval = 0.0; /* Char is at non-axial angle, ignore hints. */
00711     stems[stemno].rthintval = 0.0;
00712     ProcessHints = 0;
00713     return(0);
00714   }
00715  
00716   /* Determine orientation of stem */
00717  
00718   if (stems[stemno].vertical) {
00719     verticalondevice = !rotated;
00720     stemstart = stems[stemno].x;
00721     stemwidth = stems[stemno].dx;
00722 #ifdef DUMPDEBUGPATH
00723     if ( psfile != NULL )
00724       fprintf( psfile, "%f %f t1vstem\n", stemstart*up, stemwidth*up);
00725 #endif
00726   } else {
00727     verticalondevice = rotated;
00728     stemstart = stems[stemno].y;
00729     stemwidth = stems[stemno].dy;
00730 #ifdef DUMPDEBUGPATH
00731     if ( psfile != NULL )
00732       fprintf( psfile, "%f %f t1hstem\n", stemstart*up, stemwidth*up);
00733 #endif
00734   }
00735  
00736   /* Determine how many pixels (non-negative) correspond to 1 character space
00737      unit (unitpixels), and how many character space units (non-negative)
00738      correspond to one pixel (onepixel). */
00739  
00740   if (stems[stemno].vertical)
00741     p = ILoc(CharSpace, 1, 0);
00742   else
00743     p = ILoc(CharSpace, 0, 1);
00744   QueryLoc(p, IDENTITY, &Xpixels, &Ypixels);
00745   Destroy(p);
00746   if (verticalondevice)
00747     unitpixels = FABS(Xpixels);
00748   else
00749     unitpixels = FABS(Ypixels);
00750  
00751   onepixel = 1.0 / unitpixels;
00752 
00753   /**********************/
00754   /* ADJUST STEM WIDTHS */
00755   /**********************/
00756  
00757   widthdiff = 0.0;
00758  
00759   /* Find standard stem with smallest width difference from this stem */
00760   if (stems[stemno].vertical) { /* vertical stem */
00761     if (blues->StdVW != 0)      /* there is an entry for StdVW */
00762       widthdiff = blues->StdVW - stemwidth;
00763     for (i = 0; i < blues->numStemSnapV; ++i) { /* now look at StemSnapV */
00764       if (FABS(blues->StemSnapV[i] - stemwidth) < FABS(widthdiff))
00765         /* this standard width is the best match so far for this stem */
00766         widthdiff = blues->StemSnapV[i] - stemwidth;
00767     }
00768   } else {                      /* horizontal stem */
00769     if (blues->StdHW != 0)      /* there is an entry for StdHW */
00770       widthdiff = blues->StdHW - stemwidth;
00771     for (i = 0; i < blues->numStemSnapH; ++i) { /* now look at StemSnapH */
00772       if (FABS(blues->StemSnapH[i] - stemwidth) < FABS(widthdiff))
00773         /* this standard width is the best match so far for this stem */
00774         widthdiff = blues->StemSnapH[i] - stemwidth;
00775     }
00776   }
00777 
00778   /* Only expand or contract stems if they differ by less than 1 pixel from
00779      the closest standard width, otherwise make the width difference = 0. */
00780   if (FABS(widthdiff) > onepixel)
00781     widthdiff = 0.0;
00782  
00783   /* Expand or contract stem to the nearest integral number of pixels. */
00784   idealwidth = ROUND((stemwidth + widthdiff) * unitpixels);
00785   /* Ensure that all stems are at least one pixel wide. */
00786   if (idealwidth == 0)
00787     idealwidth = 1;
00788 
00789   /* Apply ForceBold to vertical stems. */
00790   if (blues->ForceBold && stems[stemno].vertical &&
00791       ((T1_Type1OperatorFlags & T1_IGNORE_FORCEBOLD)==0))
00792     /* Force this vertical stem to be at least DEFAULTBOLDSTEMWIDTH wide. */
00793     if (idealwidth < DEFAULTBOLDSTEMWIDTH)
00794       idealwidth = DEFAULTBOLDSTEMWIDTH;
00795   /* Now compute the number of character space units necessary */
00796   widthdiff = idealwidth * onepixel - stemwidth;
00797 
00798   /*********************************************************************/
00799   /* ALIGNMENT ZONES AND OVERSHOOT SUPPRESSION - HORIZONTAL STEMS ONLY */
00800   /*********************************************************************/
00801  
00802   stemshift = 0.0;
00803  
00804   if ( !stems[stemno].vertical ) {
00805  
00806     /* Get bottom and top boundaries of the stem. */
00807     stembottom = stemstart;
00808     stemtop = stemstart + stemwidth;
00809  
00810     /* Find out if this stem intersects an alignment zone (the BlueFuzz  */
00811     /* entry in the Private dictionary specifies the number of character */
00812     /* units to extend (in both directions) the effect of an alignment   */
00813     /* zone on a horizontal stem.  The default value of BlueFuzz is 1.   */
00814     for (i = 0; i < numalignmentzones; ++i) {
00815       if (alignmentzones[i].topzone) {
00816         if (stemtop >= alignmentzones[i].bottomy &&
00817             stemtop <= alignmentzones[i].topy + blues->BlueFuzz) {
00818           break; /* We found a top-zone */
00819         }
00820       } else {
00821         if (stembottom <= alignmentzones[i].topy &&
00822             stembottom >= alignmentzones[i].bottomy - blues->BlueFuzz) {
00823           break; /* We found a bottom-zone */
00824         }
00825       }
00826     }
00827  
00828     if (i < numalignmentzones) { /* We found an intersecting zone (number i). */
00829       suppressovershoot = FALSE;
00830       enforceovershoot = FALSE;
00831  
00832       /* When 1 character space unit is rendered smaller than BlueScale
00833          device units (pixels), we must SUPPRESS overshoots.  Otherwise,
00834          if the top (or bottom) of this stem is more than BlueShift character
00835          space units away from the flat position, we must ENFORCE overshoot. */
00836       
00837       if (unitpixels < blues->BlueScale){
00838         suppressovershoot = TRUE;
00839       }
00840       else{
00841         if (alignmentzones[i].topzone){
00842           if (stemtop >= alignmentzones[i].bottomy + blues->BlueShift){
00843             enforceovershoot = TRUE;
00844          }
00845         else
00846           if (stembottom <= alignmentzones[i].topy - blues->BlueShift){
00847             enforceovershoot = TRUE;
00848          }
00849        }
00850       }
00851       
00852       
00853       /*************************************************/
00854       /* ALIGN THE FLAT POSITION OF THE ALIGNMENT ZONE */
00855       /*************************************************/
00856  
00857       /* Compute the position of the alignment zone's flat position in
00858          device space and the amount of shift needed to align it on a
00859          pixel boundary. Move all stems this amount. */
00860  
00861       if (alignmentzones[i].topzone)
00862         flatposition = alignmentzones[i].bottomy;
00863       else
00864         flatposition = alignmentzones[i].topy;
00865  
00866       /* Find the flat position in pixels */
00867       flatpospixels = flatposition * unitpixels;
00868  
00869       /* Find the stem shift necessary to align the flat
00870          position on a pixel boundary, and use this shift for all stems */
00871       stemshift = (ROUND(flatpospixels) - flatpospixels) * onepixel;
00872  
00873       /************************************************/
00874       /* HANDLE OVERSHOOT ENFORCEMENT AND SUPPRESSION */
00875       /************************************************/
00876  
00877       /* Compute overshoot amount (non-negative) */
00878       if (alignmentzones[i].topzone)
00879         overshoot = stemtop - flatposition;
00880       else
00881         overshoot = flatposition - stembottom;
00882  
00883       if (overshoot > 0.0) {
00884         /* ENFORCE overshoot by shifting the entire stem (if necessary) so that
00885            it falls at least one pixel beyond the flat position. */
00886  
00887         if (enforceovershoot){
00888           if (overshoot < onepixel){
00889             if (alignmentzones[i].topzone)
00890               stemshift += onepixel - overshoot;
00891             else
00892               stemshift -= onepixel - overshoot;
00893          }
00894        }
00895        
00896  
00897         /* SUPPRESS overshoot by aligning the stem to the alignment zone's
00898            flat position. */
00899  
00900         if (suppressovershoot){
00901           if (alignmentzones[i].topzone)
00902             stemshift -= overshoot;
00903           else
00904             stemshift += overshoot;
00905        }
00906       }
00907  
00908       /************************************************************/
00909       /* COMPUTE HINT VALUES FOR EACH SIDE OF THE HORIZONTAL STEM */
00910       /************************************************************/
00911  
00912       /* If the stem was aligned by a topzone, we expand or contract the stem
00913          only at the bottom - since the stem top was aligned by the zone.
00914          If the stem was aligned by a bottomzone, we expand or contract the stem
00915          only at the top - since the stem bottom was aligned by the zone. */
00916       if (alignmentzones[i].topzone) {
00917         lbhintvalue = stemshift - widthdiff; /* bottom */
00918         rthintvalue = stemshift;             /* top    */
00919       } else {
00920         lbhintvalue = stemshift;             /* bottom */
00921         rthintvalue = stemshift + widthdiff; /* top    */
00922       }
00923 
00924       stems[stemno].lbhintval = lbhintvalue;
00925       stems[stemno].rthintval = rthintvalue;
00926 
00927       /* store grid-aligned stems values */
00928       stems[stemno].aly       = stemstart + lbhintvalue;
00929       stems[stemno].aldy      = stemwidth + widthdiff;
00930       
00931 #ifdef DUMPDEBUGPATH
00932       if ( psfile != NULL )
00933        fprintf( psfile, "%f %f t1alignedhstem\n", (stems[stemno].aly)*up,
00934                (stems[stemno].aldy)*up);
00935 #endif
00936       return(0);
00937  
00938     } /* endif (i < numalignmentzones) */
00939  
00940     /* We didn't find any alignment zones intersecting this stem, so
00941        proceed with normal stem alignment below. */
00942  
00943   } /* endif (!stems[stemno].vertical) */
00944  
00945   /* Align stem with pixel boundaries on device */
00946   stemstart = stemstart - widthdiff / 2;
00947   stemshift = ROUND(stemstart * unitpixels) * onepixel - stemstart;
00948  
00949   /* Adjust the boundaries of the stem */
00950   lbhintvalue = stemshift - widthdiff / 2; /* left  or bottom */
00951   rthintvalue = stemshift + widthdiff / 2; /* right or top    */
00952  
00953   if (stems[stemno].vertical) {
00954     stems[stemno].lbhintval = lbhintvalue;
00955     stems[stemno].rthintval = rthintvalue;
00956 
00957     /* store grid-aligned stem values */
00958     stems[stemno].alx       = stemstart + stemshift;
00959     stems[stemno].aldx      = stemwidth + widthdiff;
00960       
00961 #ifdef DUMPDEBUGPATH
00962     if ( psfile != NULL )
00963       fprintf( psfile, "%f %f t1alignedvstem\n", (stems[stemno].alx)*up,
00964               (stems[stemno].aldx)*up);
00965 #endif
00966   } else {
00967     stems[stemno].lbhintval = lbhintvalue;
00968     stems[stemno].rthintval = rthintvalue;
00969 
00970     /* store grid-aligned stem values */
00971     stems[stemno].aly       = stemstart + stemshift;
00972     stems[stemno].aldy      = stemwidth + widthdiff;
00973       
00974 #ifdef DUMPDEBUGPATH
00975     if ( psfile != NULL )
00976       fprintf( psfile, "%f %f t1alignedhstem\n", (stems[stemno].aly)*up,
00977               (stems[stemno].aldy)*up);
00978 #endif
00979   }
00980   return(0);
00981   
00982 }
00983  
00984 
00985 #define LEFT   1
00986 #define RIGHT  2
00987 #define BOTTOM 3
00988 #define TOP    4
00989 
00990 
00991 /***********************************************************************/
00992 /* Find the vertical and horizontal stems that the current point       */
00993 /* (x, y) may be involved in.  At most one horizontal and one vertical */
00994 /* stem can apply to a single point, since there are no overlaps       */
00995 /* allowed.                                                            */
00996 /*   The point list updated by this function.                          */
00997 /* Hints are ignored inside a DotSection.                              */
00998 /***********************************************************************/
00999 static void FindStems( double x, double y,
01000                      double dx, double dy,
01001                      double nextdx, double nextdy)
01002 {
01003   int i;
01004   int newvert, newhor;
01005   int newhorhalf, newverthalf;
01006 
01007   /* The following values will be used to decide whether a curve
01008      crosses or touches a stem in an aligned manner or not */
01009   double dtana     = 0.0;   /* tangent of pre-delta against horizontal line */ 
01010   double dtanb     = 0.0;   /* tangent of pre-delta against vertical line */ 
01011   double nextdtana = 0.0;   /* tangent of post-delta against horizontal line */ 
01012   double nextdtanb = 0.0;   /* tangent of post-delta against vertical line */ 
01013   
01014  
01015   /* setup default hinted position */
01016   ppoints[numppoints-1].ax     = ppoints[numppoints-1].x;
01017   ppoints[numppoints-1].ay     = ppoints[numppoints-1].y;
01018   if ( ppoints[numppoints-1].hinted == -1 )
01019     /* point is not to be hinted! */
01020     return;
01021   else
01022     ppoints[numppoints-1].hinted = 0;
01023 
01024   if ( InDotSection )
01025     return;
01026 
01027   if ( ProcessHints == 0 ) {
01028     return;
01029   }
01030 
01031   /* setup (absolute) tangent values and define limits that indicate nearly
01032      horizontal or nearly vertical alignment */
01033 #define NEARLYVERTICAL     0.2   /* This corresponds to about 11.3 degress deviation */
01034 #define NEARLYHORIZONTAL   0.2   /* from the ideal direction.                        */
01035   if ( dy != 0 ) {
01036     dtana = dx/dy;
01037     if ( dtanb < 0.0 )
01038       dtana = -dtana;
01039   }
01040   else
01041     dtana = NEARLYHORIZONTAL;
01042   if ( dx != 0 ) {
01043     dtanb = dy/dx;
01044     if ( dtanb < 0.0 )
01045       dtanb = -dtanb;
01046   }
01047   else
01048     dtanb = NEARLYVERTICAL;
01049   if ( nextdy != 0 ) {
01050     nextdtana = nextdx/nextdy;
01051     if ( nextdtana < 0.0 )
01052       nextdtana = -nextdtana;
01053   }
01054   else
01055     nextdtana = NEARLYHORIZONTAL;
01056   if ( nextdx != 0 ) {
01057     nextdtanb = nextdy/nextdx;
01058     if ( nextdtanb < 0.0 )
01059       nextdtanb = -nextdtanb;
01060   }
01061   else
01062     nextdtanb = NEARLYVERTICAL;
01063   
01064   newvert = newhor = -1;
01065   newhorhalf = newverthalf = -1;
01066 
01067   for (i = currstartstem; i < numstems; i++) {
01068     if (stems[i].vertical) { /* VSTEM hint */
01069       /* OK, stem is crossed in an aligned way */
01070       if ( (dtana <= NEARLYVERTICAL) || (nextdtana <= NEARLYVERTICAL)) {
01071        if ((x >= stems[i].x ) &&
01072            (x <= stems[i].x+stems[i].dx )) {
01073          newvert = i;
01074          if (x < stems[i].x+stems[i].dx / 2)
01075            newverthalf = LEFT;
01076          else
01077            newverthalf = RIGHT;
01078        }
01079       }
01080     }
01081     else {                 /* HSTEM hint */
01082       if ( (dtanb <= NEARLYHORIZONTAL) || (nextdtanb <= NEARLYHORIZONTAL)) {
01083        /* OK, stem is crossed in an aligned way */
01084        if ((y >= stems[i].y ) &&
01085            (y <= stems[i].y+stems[i].dy )) {
01086          newhor = i;
01087          if (y < stems[i].y+stems[i].dy / 2)
01088            newhorhalf = BOTTOM;
01089          else
01090            newhorhalf = TOP;
01091        }
01092       }
01093     }
01094   }
01095 
01096   if ( newvert != -1 ) {
01097     /* mark the latest point in the point list to be v-hinted! */
01098     if ( newverthalf == LEFT ) {
01099       /* left hint */
01100       ppoints[numppoints-1].ax  += stems[newvert].lbhintval;
01101     }
01102     else {
01103        /* right hint */
01104       ppoints[numppoints-1].ax  += stems[newvert].rthintval;
01105     }
01106     ppoints[numppoints-1].hinted |= 0x01;
01107   }
01108   if ( newhor != -1 ) {
01109     /* mark the latest point in the point list to be h-hinted! */
01110     if ( newhorhalf == BOTTOM ) {
01111       /* bottom hint */
01112       ppoints[numppoints-1].ay  += stems[newhor].lbhintval;
01113     }
01114     else {
01115        /* top hint */
01116       ppoints[numppoints-1].ay  += stems[newhor].rthintval;
01117     }
01118     ppoints[numppoints-1].hinted |= 0x02;
01119   }
01120   
01121   return;
01122   
01123 }
01124 
01125 
01126 /* Type 1 internal functions */
01127 static int ClearStack()
01128 {
01129   Top = -1;
01130   return(0);
01131   
01132 }
01133  
01134 static int Push(Num)
01135         DOUBLE Num;
01136 {
01137   if (++Top < MAXSTACK) Stack[Top] = Num;
01138   else Error0i("Push: Stack full\n");
01139   return(0);
01140   
01141 }
01142  
01143 static int ClearCallStack()
01144 {
01145   CallTop = -1;
01146   return(0);
01147 }
01148  
01149 static int PushCall(CurrStrP, CurrIndex, CurrKey)
01150   psobj *CurrStrP;
01151   int CurrIndex;
01152   unsigned short CurrKey;
01153 {
01154   if (++CallTop < MAXCALLSTACK) {
01155     CallStack[CallTop].currstrP = CurrStrP;   /* save CharString pointer */
01156     CallStack[CallTop].currindex = CurrIndex; /* save CharString index */
01157     CallStack[CallTop].currkey = CurrKey;     /* save decryption key */
01158   }
01159   else Error0i("PushCall: Stack full\n");
01160   return(0);
01161 }
01162  
01163 static int PopCall(CurrStrPP, CurrIndexP, CurrKeyP)
01164   psobj **CurrStrPP;
01165   int *CurrIndexP;
01166   unsigned short *CurrKeyP;
01167 {
01168   if (CallTop >= 0) {
01169     *CurrStrPP = CallStack[CallTop].currstrP; /* restore CharString pointer */
01170     *CurrIndexP = CallStack[CallTop].currindex; /* restore CharString index */
01171     *CurrKeyP = CallStack[CallTop--].currkey;   /* restore decryption key */
01172   }
01173   else Error0i("PopCall: Stack empty\n");
01174   return(0);
01175 }
01176 
01177  
01178 static int ClearPSFakeStack()
01179 {
01180   PSFakeTop = -1;
01181   return(0);
01182 }
01183  
01184 /* PSFakePush: Pushes a number onto the fake PostScript stack */
01185 static int PSFakePush(Num)
01186   DOUBLE Num;
01187 {
01188   if (++PSFakeTop < MAXPSFAKESTACK) PSFakeStack[PSFakeTop] = Num;
01189   else Error0i("PSFakePush: Stack full\n");
01190   return(0);
01191 }
01192  
01193 /* PSFakePop: Removes a number from the top of the fake PostScript stack */
01194 static DOUBLE PSFakePop ()
01195 {
01196   if (PSFakeTop >= 0) return(PSFakeStack[PSFakeTop--]);
01197   
01198   else Error0d("PSFakePop : Stack empty\n");
01199 
01200   /*NOTREACHED*/
01201 }
01202  
01203 /***********************************************************************/
01204 /* Center a stem on the pixel grid -- used by HStem3 and VStem3        */
01205 /***********************************************************************/
01206 static struct segment *CenterStem(edge1, edge2)
01207     DOUBLE edge1;
01208     DOUBLE edge2;
01209 {
01210   int idealwidth, verticalondevice;
01211   DOUBLE leftx, lefty, rightx, righty, center, width;
01212   DOUBLE widthx, widthy;
01213   DOUBLE shift, shiftx, shifty;
01214   DOUBLE Xpixels, Ypixels;
01215   struct segment *p;
01216  
01217   p = Loc(CharSpace, edge1, 0.0);
01218   QueryLoc(p, IDENTITY, &leftx, &lefty);
01219  
01220   p = Join(p, Loc(CharSpace, edge2, 0.0));
01221   QueryLoc(p, IDENTITY, &rightx, &righty);
01222   Destroy(p);
01223  
01224   widthx = FABS(rightx - leftx);
01225   widthy = FABS(righty - lefty);
01226  
01227   if (widthy <= EPS) { /* verticalondevice hint */
01228     verticalondevice = TRUE;
01229     center = (rightx + leftx) / 2.0;
01230     width = widthx;
01231   }
01232   else if (widthx <= EPS) { /* horizontal hint */
01233     verticalondevice = FALSE;
01234     center = (righty + lefty) / 2.0;
01235     width = widthy;
01236   }
01237   else { /* neither horizontal nor verticalondevice and not oblique */
01238     return (NULL);
01239   }
01240  
01241   idealwidth = ROUND(width);
01242   if (idealwidth == 0) idealwidth = 1;
01243   if (ODD(idealwidth)) {       /* is ideal width odd? */
01244     /* center stem over pixel */
01245     shift = FLOOR(center) + 0.5 - center;
01246   }
01247   else {
01248     /* align stem on pixel boundary */
01249     shift = ROUND(center) - center;
01250   }
01251  
01252   if (verticalondevice) {
01253     shiftx = shift;
01254     shifty = 0.0;
01255   } else {
01256     shifty = shift;
01257     shiftx = 0.0;
01258   }
01259  
01260   p = Loc(IDENTITY, shiftx, shifty);
01261   QueryLoc(p, CharSpace, &Xpixels, &Ypixels);
01262   wsoffsetX = Xpixels; wsoffsetY = Ypixels;
01263   currx += wsoffsetX; curry += wsoffsetY;
01264  
01265   return (p);
01266 }
01267  
01268 /*-----------------------------------------------------------------------
01269   Decrypt - From Adobe Type 1 book page 63, with some modifications
01270 -----------------------------------------------------------------------*/
01271 #define KEY 4330 /* Initial key (seed) for CharStrings decryption */
01272 #define C1 52845 /* Multiplier for pseudo-random number generator */
01273 #define C2 22719 /* Constant for pseudo-random number generator */
01274  
01275 static unsigned short r; /* Pseudo-random sequence of keys */
01276  
01277 static unsigned char Decrypt(cipher)
01278 unsigned char cipher;
01279 {
01280   unsigned char plain;
01281  
01282   plain = cipher ^ (r >> 8);
01283   r = (cipher + r) * C1 + C2;
01284   return plain;
01285 }
01286  
01287 /* Get the next byte from the codestring being interpreted */
01288 static int DoRead(CodeP)
01289   int *CodeP;
01290 {
01291   if (strindex >= CharStringP->len) return(FALSE); /* end of string */
01292   /* We handle the non-documented Adobe convention to use lenIV=-1 to
01293      suppress charstring encryption. */
01294   if (blues->lenIV==-1) {
01295     *CodeP = (unsigned char) CharStringP->data.stringP[strindex++];
01296   }
01297   else { 
01298     *CodeP = Decrypt((unsigned char) CharStringP->data.stringP[strindex++]);
01299   }
01300   
01301   return(TRUE);
01302 }
01303  
01304 /* Strip blues->lenIV bytes from CharString and update encryption key */
01305 /* (the lenIV entry in the Private dictionary specifies the number of */
01306 /* random bytes at the beginning of each CharString; default is 4)    */
01307 static void StartDecrypt()
01308 {
01309   int Code;
01310  
01311   r = KEY; /* Initial key (seed) for CharStrings decryption */
01312   for (strindex = 0; strindex < blues->lenIV;){
01313     if (!DoRead(&Code)) /* Read a byte and update decryption key */
01314       Error0v("StartDecrypt: Premature end of CharString\n");
01315   }
01316   
01317 }
01318 
01319 #undef DecodeDebug
01320 
01321 static int Decode(Code)
01322   int Code;
01323 {
01324   int Code1, Code2, Code3, Code4;
01325  
01326   if (Code <= 31){                           /* Code is [0,31]    */
01327 #ifdef DecodeDebug
01328     fprintf(stderr, "Decode: Code=%d -> Command\n", Code);
01329 #endif
01330     DoCommand(Code);
01331   }
01332   else if (Code <= 246){                     /* Code is [32,246]  */
01333 #ifdef DecodeDebug
01334     fprintf(stderr, "Decode: Code=%d -> number=%f\n",
01335            Code, (DOUBLE)(Code-139));
01336 #endif
01337     Push((DOUBLE)(Code - 139));
01338   }
01339   else if (Code <= 250) {                   /* Code is [247,250] */
01340     if (!DoRead(&Code2)) goto ended;
01341 #ifdef DecodeDebug
01342     fprintf(stderr, "Decode: Code=%d next Code=%d -> number=%f\n",
01343            Code, Code2, (DOUBLE)(((Code - 247) << 8) + Code2 + 108));
01344 #endif
01345     Push((DOUBLE)(((Code - 247) << 8) + Code2 + 108));
01346   }
01347   else if (Code <= 254) {                   /* Code is [251,254] */
01348     if (!DoRead(&Code2)) goto ended;
01349 #ifdef DecodeDebug
01350     fprintf(stderr, "Decode: Code=%d, next Code=%d -> number=%f\n",
01351            Code, Code2, (DOUBLE)( -((Code - 251) << 8) - Code2 - 108));
01352 #endif
01353     Push((DOUBLE)( -((Code - 251) << 8) - Code2 - 108));
01354   }
01355   else {                                    /* Code is 255 */
01356     if (!DoRead(&Code1)) goto ended;
01357     if (!DoRead(&Code2)) goto ended;
01358     if (!DoRead(&Code3)) goto ended;
01359     if (!DoRead(&Code4)) goto ended;
01360 #ifdef DecodeDebug
01361     fprintf(stderr, "Decode: Code=255, Code1=%d, Code2=%d, Code3=%d, Code4=%d -> number=%f\n",
01362            Code1, Code2, Code3, Code4,
01363            (DOUBLE)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
01364 #endif
01365     Push((DOUBLE)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
01366   }
01367   return(0);
01368  
01369 ended: Error0i("Decode: Premature end of Type 1 CharString");
01370 }
01371 
01372 #undef DoCommandDebug 
01373 
01374 /* Interpret a command code */
01375 static int DoCommand(Code)
01376   int Code;
01377 {
01378   switch(Code) {
01379     case HSTEM: /* |- y dy HSTEM |- */
01380 #ifdef DoCommandDebug
01381       printf("DoCommand: HStem\n");
01382 #endif
01383       /* Vertical range of a horizontal stem zone */
01384       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01385       HStem(Stack[0], Stack[1]);
01386       ClearStack();
01387       break;
01388     case VSTEM: /* |- x dx VSTEM |- */
01389 #ifdef DoCommandDebug
01390       printf("DoCommand: VStem\n");
01391 #endif
01392       /* Horizontal range of a vertical stem zone */
01393       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01394       VStem(Stack[0], Stack[1]);
01395       ClearStack();
01396       break;
01397     case VMOVETO: /* |- dy VMOVETO |- */
01398 #ifdef DoCommandDebug
01399       printf("DoCommand: VMoveto\n");
01400 #endif
01401       /* Vertical MOVETO, equivalent to 0 dy RMOVETO */
01402       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01403       RMoveTo(0.0, Stack[0]);
01404       ClearStack();
01405       break;
01406     case RLINETO: /* |- dx dy RLINETO |- */
01407 #ifdef DoCommandDebug
01408       printf("DoCommand: RLineto\n");
01409 #endif
01410       /* Like RLINETO in PostScript */
01411       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01412       RLineTo(Stack[0], Stack[1]);
01413       ClearStack();
01414       break;
01415     case HLINETO: /* |- dx HLINETO |- */
01416 #ifdef DoCommandDebug
01417       printf("DoCommand: HLineto\n");
01418 #endif
01419       /* Horizontal LINETO, equivalent to dx 0 RLINETO */
01420       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01421       RLineTo(Stack[0], 0.0);
01422       ClearStack();
01423       break;
01424     case VLINETO: /* |- dy VLINETO |- */
01425 #ifdef DoCommandDebug
01426       printf("DoCommand: VLineto\n");
01427 #endif
01428       /* Vertical LINETO, equivalent to 0 dy RLINETO */
01429       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01430       RLineTo(0.0, Stack[0]);
01431       ClearStack();
01432       break;
01433     case RRCURVETO:
01434 #ifdef DoCommandDebug
01435       printf("DoCommand: RRCurveto\n");
01436 #endif
01437       /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
01438       /* Relative RCURVETO, equivalent to dx1 dy1 */
01439       /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
01440       /* (dy1+dy2+dy3) RCURVETO in PostScript */
01441       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01442       RRCurveTo(Stack[0], Stack[1], Stack[2], Stack[3],
01443         Stack[4], Stack[5]);
01444       ClearStack();
01445       break;
01446     case CLOSEPATH: /* - CLOSEPATH |- */
01447 #ifdef DoCommandDebug
01448       printf("DoCommand: ClosePath\n");
01449 #endif
01450       /* Closes a subpath without repositioning the */
01451       /* current point */
01452       DoClosePath();
01453       ClearStack();
01454       break;
01455     case CALLSUBR: /* subr# CALLSUBR - */
01456 #ifdef DoCommandDebug
01457       printf("DoCommand: CallSubr\n");
01458 #endif
01459       /* Calls a CharString subroutine with index */
01460       /* subr# from the Subrs array */
01461       if (Top < 0) Error1i("DoCommand: Stack low\n (Code=%d)", Code);
01462       CallSubr((int)Stack[Top--]);
01463       break;
01464     case RETURN: /* - RETURN - */
01465 #ifdef DoCommandDebug
01466       printf("DoCommand: Return\n");
01467 #endif
01468       /* Returns from a Subrs array CharString */
01469       /* subroutine called with CALLSUBR */
01470       Return();
01471       break;
01472     case ESCAPE: /* ESCAPE to two-byte command code */
01473 #ifdef DoCommandDebug
01474       printf("DoCommand: Escape to 2 Byte Code (Code=%d)\n", Code);
01475 #endif
01476       if (!DoRead(&Code)) Error0i("DoCommand: ESCAPE is last byte\n");
01477       Escape(Code);
01478       break;
01479     case HSBW: /* |- sbx wx HSBW |- */
01480 #ifdef DoCommandDebug
01481       printf("DoCommand: HSBW\n");
01482 #endif
01483       /* Set the left sidebearing point to (sbx,0), */
01484       /* set the character width vector to (wx,0). */
01485       /* Equivalent to sbx 0 wx 0 SBW.  Space */
01486       /* character should have sbx = 0 */
01487       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01488       Sbw(Stack[0], 0.0, Stack[1], 0.0);
01489       ClearStack();
01490       break;
01491     case ENDCHAR: /* - ENDCHAR |- */
01492 #ifdef DoCommandDebug
01493       printf("DoCommand: EndChar\n");
01494 #endif
01495       /* Finishes a CharString outline */
01496       EndChar();
01497       ClearStack();
01498       break;
01499     case RMOVETO: /* |- dx dy RMOVETO |- */
01500 #ifdef DoCommandDebug
01501       printf("DoCommand: RMoveto\n");
01502 #endif
01503       /* Behaves like RMOVETO in PostScript */
01504       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01505       RMoveTo(Stack[0], Stack[1]);
01506       ClearStack();
01507       break;
01508     case HMOVETO: /* |- dx HMOVETO |- */
01509 #ifdef DoCommandDebug
01510       printf("DoCommand: HMovetoUnassigned\n");
01511 #endif
01512       /* Horizontal MOVETO. Equivalent to dx 0 RMOVETO */
01513       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01514       RMoveTo(Stack[0], 0.0);
01515       ClearStack();
01516       break;
01517     case VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
01518 #ifdef DoCommandDebug
01519       printf("DoCommand: VHCurveto\n");
01520 #endif
01521       /* Vertical-Horizontal CURVETO, equivalent to */
01522       /* 0 dy1 dx2 dy2 dx3 0 RRCURVETO */
01523       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01524       RRCurveTo(0.0, Stack[0], Stack[1], Stack[2],
01525               Stack[3], 0.0);
01526       ClearStack();
01527       break;
01528     case HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
01529 #ifdef DoCommandDebug
01530       printf("DoCommand: HCurveto\n");
01531 #endif
01532       /* Horizontal-Vertical CURVETO, equivalent to */
01533       /* dx1 0 dx2 dy2 0 dy3 RRCURVETO */
01534       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01535       RRCurveTo(Stack[0], 0.0, Stack[1], Stack[2], 0.0, Stack[3]);
01536       ClearStack();
01537       break;
01538   case UNKNOWN_15:
01539     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01540     ClearStack();
01541     break;
01542     default: /* Unassigned command code */
01543 #ifdef DoCommandDebug
01544       printf("DoCommand: Unassigned\n");
01545 #endif
01546       ClearStack();
01547       Error1i("DoCommand: Unassigned code %d\n", Code);
01548   }
01549   return(0);
01550   
01551 }
01552  
01553 static int Escape(Code)
01554   int Code;
01555 {
01556   int i, Num;
01557   struct segment *p;
01558  
01559   switch(Code) {
01560     case DOTSECTION: /* - DOTSECTION |- */
01561       /* Brackets an outline section for the dots in */
01562       /* letters such as "i", "j", and "!". */
01563       DotSection();
01564       ClearStack();
01565       break;
01566     case VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
01567       /* Declares the horizontal ranges of three */
01568       /* vertical stem zones between x0 and x0+dx0, */
01569       /* x1 and x1+dx1, and x2 and x2+dx2. */
01570       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01571       if (!wsset && ProcessHints) {
01572         /* Shift the whole character so that the middle stem is centered. */
01573         p = CenterStem(Stack[2] + sidebearingX, Stack[3]);
01574         path = Join(path, p);
01575         wsset = 1;
01576       }
01577  
01578       VStem(Stack[0], Stack[1]);
01579       VStem(Stack[2], Stack[3]);
01580       VStem(Stack[4], Stack[5]);
01581       ClearStack();
01582       break;
01583     case HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
01584       /* Declares the vertical ranges of three hori- */
01585       /* zontal stem zones between y0 and y0+dy0, */
01586       /* y1 and y1+dy1, and y2 and y2+dy2. */
01587       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01588       HStem(Stack[0], Stack[1]);
01589       HStem(Stack[2], Stack[3]);
01590       HStem(Stack[4], Stack[5]);
01591       ClearStack();
01592       break;
01593     case SEAC: /* |- asb adx ady bchar achar SEAC |- */
01594       /* Standard Encoding Accented Character. */
01595       if (Top < 4) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01596       Seac(Stack[0], Stack[1], Stack[2],
01597         (unsigned char) Stack[3],
01598         (unsigned char) Stack[4]);
01599       ClearStack();
01600       break;
01601     case SBW: /* |- sbx sby wx wy SBW |- */
01602       /* Set the left sidebearing point to (sbx,sby), */
01603       /* set the character width vector to (wx,wy). */
01604       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01605       Sbw(Stack[0], Stack[1], Stack[2], Stack[3]);
01606       ClearStack();
01607       break;
01608     case DIV: /* num1 num2 DIV quotient */
01609       /* Behaves like DIV in the PostScript language */
01610       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01611       Stack[Top-1] = Div(Stack[Top-1], Stack[Top]);
01612       Top--;
01613       break;
01614     case CALLOTHERSUBR:
01615       /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
01616       /* Make calls on the PostScript interpreter */
01617       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01618       Num = Stack[Top-1];
01619       if (Top < Num+1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01620       for (i = 0; i < Num; i++) PSFakePush(Stack[Top - i - 2]);
01621       Top -= Num + 2;
01622       CallOtherSubr((int)Stack[Top + Num + 2]);
01623       break;
01624     case POP: /* - POP number */
01625       /* Removes a number from the top of the */
01626       /* PostScript interpreter stack and pushes it */
01627       /* onto the Type 1 BuildChar operand stack */
01628       Push(PSFakePop());
01629       break;
01630   case SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
01631     /* Sets the current point to (x,y) in absolute */
01632     /* character space coordinates without per- */
01633     /* forming a CharString MOVETO command */
01634     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01635     SetCurrentPoint(Stack[0], Stack[1]);
01636     ClearStack();
01637     break;
01638   case UNKNOWN_15:
01639     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
01640     ClearStack();
01641     break;
01642   default: /* Unassigned escape code command */
01643     ClearStack();
01644     Error1i("Escape: Unassigned code %d\n", Code);
01645   }
01646   return(0);
01647 
01648 }
01649  
01650 /* |- y dy HSTEM |- */
01651 /* Declares the vertical range of a horizontal stem zone */
01652 /* between coordinates y and y + dy */
01653 /* y is relative to the left sidebearing point */
01654 static int HStem(y, dy)
01655   DOUBLE y, dy;
01656 {
01657   IfTrace2((FontDebug), "Hstem %f %f\n", y, dy);
01658   if (ProcessHints) {
01659     if (numstems >= MAXSTEMS) Error0i("HStem: Too many hints\n");
01660     if (dy < 0.0) {y += dy; dy = -dy;}
01661     stems[numstems].vertical = FALSE;
01662     stems[numstems].x = 0.0;
01663     stems[numstems].y = sidebearingY + y + wsoffsetY;
01664     stems[numstems].dx = 0.0;
01665     stems[numstems].dy = dy;
01666     ComputeStem(numstems);
01667     numstems++;
01668   }
01669   return(0);
01670 }
01671  
01672 /* |- x dx VSTEM |- */
01673 /* Declares the horizontal range of a vertical stem zone */
01674 /* between coordinates x and x + dx */
01675 /* x is relative to the left sidebearing point */
01676 static int VStem(x, dx)
01677   DOUBLE x, dx;
01678 {
01679   IfTrace2((FontDebug), "Vstem %f %f\n", x, dx);
01680   if (ProcessHints) {
01681     if (numstems >= MAXSTEMS) Error0i("VStem: Too many hints\n");
01682     if (dx < 0.0) {x += dx; dx = -dx;}
01683     stems[numstems].vertical = TRUE;
01684     stems[numstems].x = sidebearingX + x + wsoffsetX;
01685     stems[numstems].y = 0.0;
01686     stems[numstems].dx = dx;
01687     stems[numstems].dy = 0.0;
01688     ComputeStem(numstems);
01689     numstems++;
01690   }
01691   return(0);
01692 }
01693  
01694 /* |- dx dy RLINETO |- */
01695 /* Behaves like RLINETO in PostScript */
01696 static int RLineTo(dx, dy)
01697   DOUBLE dx, dy;
01698 {
01699   long pindex = 0;
01700   
01701   /* compute hinting for previous segment! */
01702   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx, dy);
01703 
01704   /* Allocate a new path point and pre-setup data */
01705   pindex = nextPPoint();
01706   ppoints[pindex].x       = currx + dx;
01707   ppoints[pindex].y       = curry + dy;
01708   ppoints[pindex].ax      = ppoints[pindex].x;
01709   ppoints[pindex].ay      = ppoints[pindex].y;
01710   ppoints[pindex].type    = PPOINT_LINE;
01711   ppoints[pindex].hinted  = 0;
01712 
01713   /* update ideal position */
01714   currx                  += dx;
01715   curry                  += dy;
01716 
01717   return(0);
01718 }
01719  
01720 /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
01721 /* Relative RCURVETO, equivalent to dx1 dy1 */
01722 /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
01723 /* (dy1+dy2+dy3) RCURVETO in PostScript */
01724 static int RRCurveTo(dx1, dy1, dx2, dy2, dx3, dy3)
01725   DOUBLE dx1, dy1, dx2, dy2, dx3, dy3;
01726 {
01727   long pindex = 0;
01728   
01729   /* compute hinting for previous point! */
01730   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx1, dy1);
01731 
01732   /* Allocate three new path points and pre-setup data */
01733   pindex = nextPPoint();
01734   ppoints[pindex].x       = currx + dx1;
01735   ppoints[pindex].y       = curry + dy1;
01736   ppoints[pindex].ax      = ppoints[pindex].x;
01737   ppoints[pindex].ay      = ppoints[pindex].y;
01738   ppoints[pindex].type    = PPOINT_BEZIER_B;
01739   ppoints[pindex].hinted  = 0;
01740 
01741   /* update ideal position */
01742   currx                  += dx1;
01743   curry                  += dy1;
01744 
01745   /* compute hinting for previous point! */
01746   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx2, dy2);
01747 
01748   pindex = nextPPoint();
01749   ppoints[pindex].x       = currx + dx2;
01750   ppoints[pindex].y       = curry + dy2;
01751   ppoints[pindex].ax      = ppoints[pindex].x;
01752   ppoints[pindex].ay      = ppoints[pindex].y;
01753   ppoints[pindex].type    = PPOINT_BEZIER_C;
01754   ppoints[pindex].hinted  = 0;
01755 
01756   /* update ideal position */
01757   currx                  += dx2;
01758   curry                  += dy2;
01759 
01760   /* compute hinting for previous point! */
01761   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx3, dy3);
01762 
01763   pindex = nextPPoint();
01764   ppoints[pindex].x       = currx + dx3;
01765   ppoints[pindex].y       = curry + dy3;
01766   ppoints[pindex].ax      = ppoints[pindex].x;
01767   ppoints[pindex].ay      = ppoints[pindex].y;
01768   ppoints[pindex].type    = PPOINT_BEZIER_D;
01769   ppoints[pindex].hinted  = 0;
01770 
01771   /* update ideal position */
01772   currx                  += dx3;
01773   curry                  += dy3;
01774 
01775   return(0);
01776 }
01777  
01778 /* - CLOSEPATH |- */
01779 /* Closes a subpath WITHOUT repositioning the */
01780 /* current point */
01781 static int DoClosePath()
01782 {
01783   long pindex = 0;
01784   long i = 0;
01785   long tmpind;
01786   double deltax = 0.0;
01787   double deltay = 0.0;
01788   
01789   /* If this ClosePath command together with the starting point of this
01790      path completes to a segment aligned to a stem, we would miss
01791      hinting for this point. --> Check and explicitly care for this! */
01792   /* 1. Step back in the point list to the last moveto-point */
01793   i = numppoints - 1;
01794   while ( (i > 0) && (ppoints[i].type != PPOINT_MOVE ) ) { 
01795     --i; 
01796   }
01797   
01798   /* 2. Re-hint starting point and hint current point */
01799   if ( ppoints[i].type == PPOINT_MOVE) {
01800     deltax = ppoints[i].x - ppoints[numppoints-1].x;
01801     deltay = ppoints[i].y - ppoints[numppoints-1].y;
01802 
01803     /* save nummppoints and reset to move point */
01804     tmpind = numppoints;
01805     numppoints = i + 1;
01806     
01807     /* re-hint starting point of current subpath (uses the value of numppoints!) */
01808     FindStems( ppoints[i].x, ppoints[i].y, deltax, deltay,
01809               ppoints[i+1].x-ppoints[i].x, ppoints[i+1].y-ppoints[i].y);
01810 
01811     /* restore numppoints and setup hinting for current point */
01812     numppoints = tmpind;
01813     FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y,
01814               deltax, deltay);
01815   }
01816   
01817   /* Allocate a new path point and pre-setup data */
01818   pindex = nextPPoint();
01819   ppoints[pindex].x       = currx;
01820   ppoints[pindex].y       = curry;
01821   ppoints[pindex].ax      = ppoints[pindex-1].x;
01822   ppoints[pindex].ay      = ppoints[pindex-1].y;
01823   ppoints[pindex].type    = PPOINT_CLOSEPATH;
01824   ppoints[pindex].hinted  = 0;
01825 
01826   return(0);
01827 }
01828  
01829 /* subr# CALLSUBR - */
01830 /* Calls a CharString subroutine with index */
01831 /* subr# from the Subrs array */
01832 static int CallSubr(subrno)
01833   int subrno;
01834 {
01835   IfTrace2((FontDebug), "CallSubr %d (CallStackSize=%d)\n", subrno, CallTop);
01836   if ((subrno < 0) || (subrno >= SubrsP->len))
01837     Error0i("CallSubr: subrno out of range\n");
01838   PushCall(CharStringP, strindex, r);
01839   CharStringP = &SubrsP->data.arrayP[subrno];
01840   StartDecrypt();
01841   return(0);
01842 }
01843  
01844 /* - RETURN - */
01845 /* Returns from a Subrs array CharString */
01846 /* subroutine called with CALLSUBR */
01847 static int Return()
01848 {
01849   IfTrace0((FontDebug), "Return\n");
01850   PopCall(&CharStringP, &strindex, &r);
01851   return(0);
01852 }
01853  
01854 /* - ENDCHAR |- */
01855 /* Finishes a CharString outline */
01856 /* Executes SETCHACHEDEVICE using a bounding box */
01857 /* it computes directly from the character outline */
01858 /* and using the width information acquired from a previous */
01859 /* HSBW or SBW.  It then calls a special version of FILL */
01860 /* or STROKE depending on the value of PaintType in the */
01861 /* font dictionary */
01862 static int EndChar()
01863 {
01864   long pindex = 0;
01865   
01866   IfTrace0((FontDebug), "EndChar\n");
01867  
01868   /* There is no need to compute and set bounding box for
01869      the cache, since XIMAGER does that on the fly. */
01870  
01871   /* Allocate a new path point and pre-setup data.
01872      Note: For this special case, we use the variables that usually
01873      store hinted coordinates for the escapement of the character.
01874      It is required in handleCurrentSegment().
01875   */
01876   pindex = nextPPoint();
01877   ppoints[pindex].x       = currx;
01878   ppoints[pindex].y       = curry;
01879   ppoints[pindex].ax      = escapementX;
01880   ppoints[pindex].ay      = escapementY;
01881   ppoints[pindex].type    = PPOINT_ENDCHAR;
01882   ppoints[pindex].hinted  = -1;
01883 
01884   return(0);
01885  
01886 }
01887  
01888 /* |- dx dy RMOVETO |- */
01889 /* Behaves like RMOVETO in PostScript */
01890 static int RMoveTo(dx,dy)
01891   DOUBLE dx,dy;
01892 {
01893   long pindex = 0;
01894 
01895   /* Compute hinting for this path point! */
01896   if ( numppoints == 1 ) {
01897     /* Since RMoveTo for this case starts a new path segment
01898        (flex-constructs have already been handled), the current
01899        point is hinted here only taking the next point into account,
01900        but not the previous. Later on, in DoClosePath(), we'll step
01901        back to this point and the position might be rehinted. */
01902     FindStems( currx, curry, 0, 0, dx, dy);
01903   }
01904   else {
01905     FindStems( currx, curry, ppoints[numppoints-2].x, ppoints[numppoints-2].y, dx, dy);
01906   }
01907   
01908 
01909 
01910   /* Allocate a new path point and pre-setup data */
01911   pindex = nextPPoint();
01912   ppoints[pindex].x       = currx + dx;
01913   ppoints[pindex].y       = curry + dy;
01914   ppoints[pindex].ax      = ppoints[pindex].x;
01915   ppoints[pindex].ay      = ppoints[pindex].y;
01916   ppoints[pindex].type    = PPOINT_MOVE;
01917   ppoints[pindex].hinted  = 0;
01918 
01919   /* update ideal position */
01920   currx                  += dx;
01921   curry                  += dy;
01922   
01923   return 0;
01924 }
01925  
01926 /* - DOTSECTION |- */
01927 /* Brackets an outline section for the dots in */
01928 /* letters such as "i", "j", and "!". */
01929 static int DotSection()
01930 {
01931   IfTrace0((FontDebug), "DotSection\n");
01932   InDotSection = !InDotSection;
01933   return(0);
01934 }
01935  
01936 /* |- asb adx ady bchar achar SEAC |- */
01937 /* Standard Encoding Accented Character. */
01938 static int Seac(asb, adx, ady, bchar, achar)
01939   DOUBLE asb, adx, ady;
01940   unsigned char bchar, achar;
01941 {
01942   int Code;
01943   long pindex = 0;
01944   
01945   IfTrace4((FontDebug), "SEAC %f %f %f %d ", asb, adx, ady, bchar);
01946   IfTrace1((FontDebug), "%d\n", achar);
01947  
01948   /* Move adx - asb, ady over and up from base char's sbpoint. */
01949   /* (We use adx - asb to counteract the accents sb shift.) */
01950   /* The variables accentoffsetX/Y modify sidebearingX/Y in Sbw(). */
01951   /* Note that these incorporate the base character's sidebearing shift by */
01952   /* using the current sidebearingX, Y values. */
01953   accentoffsetX = adx - asb;
01954   accentoffsetY = ady;
01955 
01956   /* Set path = NULL to avoid complaints from Sbw(). */
01957   path = NULL;
01958  
01959   /* Go find the CharString for the accent's code via an upcall */
01960   CharStringP = GetType1CharString(Environment, achar);
01961   if (CharStringP == NULL) {
01962      Error1i("Invalid accent ('%03o) in SEAC\n", achar);
01963   }
01964   StartDecrypt();
01965  
01966   ClearStack();
01967   ClearPSFakeStack();
01968   ClearCallStack();
01969  
01970   for (;;) {
01971     if (!DoRead(&Code)) break;
01972     Decode(Code);
01973     if (errflag) return(0);
01974   }
01975 
01976   /* Allocate a new path point. Data in this case is not relevant
01977      in handleSegment(), we merely insert a snap() in order to return
01978      to origin of the accent char. */
01979   pindex = nextPPoint();
01980   ppoints[pindex].x       = accentoffsetX;
01981   ppoints[pindex].y       = accentoffsetY;
01982   ppoints[pindex].ax      = accentoffsetX;
01983   ppoints[pindex].ay      = accentoffsetY;
01984   ppoints[pindex].type    = PPOINT_SEAC;
01985   ppoints[pindex].hinted  = 0;
01986 
01987   /* We must reset these to null now. */
01988   accentoffsetX = accentoffsetY = 0;
01989  
01990   /* go find the CharString for the base char's code via an upcall */
01991   CharStringP = GetType1CharString(Environment, bchar);
01992   StartDecrypt();
01993  
01994   ClearStack();
01995   ClearPSFakeStack();
01996   ClearCallStack();
01997  
01998   InitStems();
01999  
02000   for (;;) {
02001     if (!DoRead(&Code)) break;
02002     Decode(Code);
02003     if (errflag) return(0);
02004   }
02005 
02006   return(0);
02007 }
02008  
02009  
02010 /* |- sbx sby wx wy SBW |- */
02011 /* Set the left sidebearing point to (sbx,sby), */
02012 /* set the character width vector to (wx,wy). */
02013 static int Sbw(sbx, sby, wx, wy)
02014   DOUBLE sbx, sby, wx, wy;
02015 {
02016   long pindex = 0;
02017   
02018   
02019   IfTrace4((FontDebug), "SBW %f %f %f %f\n", sbx, sby, wx, wy);
02020  
02021   escapementX = wx; /* Character width vector */
02022   escapementY = wy;
02023  
02024   /* Sidebearing values are sbx, sby args, plus accent offset from Seac(). */
02025   sidebearingX = sbx + accentoffsetX;
02026   sidebearingY = sby + accentoffsetY;
02027  
02028   currx = sidebearingX;
02029   curry = sidebearingY;
02030   /*
02031   path = Join(path, Loc(CharSpace, sidebearingX, sidebearingY));
02032   if (ProcessHints) {
02033     hcurrx = sidebearingX;
02034     hcurry = sidebearingY;
02035   }
02036   */
02037   
02038   /* Allocate a path point and setup.
02039      Note: In this special case, we store the char escapement in the members
02040            ax and ay. They are required in handleCurrentSegment(). Hinting
02041           is not required for SBW, anyhow!
02042   */
02043   pindex = nextPPoint();
02044   ppoints[pindex].x       = currx;
02045   ppoints[pindex].y       = curry;
02046   ppoints[pindex].ax      = wx;
02047   ppoints[pindex].ay      = wy;
02048   ppoints[pindex].type    = PPOINT_SBW;
02049   ppoints[pindex].hinted  = -1; /* indicate that point is not to be hinted */
02050  
02051   return(0);
02052 }
02053  
02054  /* num1 num2 DIV quotient */
02055 /* Behaves like DIV in the PostScript language */
02056 static DOUBLE Div(num1, num2)
02057   DOUBLE num1, num2;
02058 {
02059   IfTrace2((FontDebug), "Div %f %f\n", num1, num2);
02060   return(num1 / num2);
02061 }
02062  
02063 /*
02064   The following four subroutines (FlxProc, FlxProc1, FlxProc2, and
02065   HintReplace) are C versions of the OtherSubrs Programs, which were
02066   were published in the Adobe Type 1 Font Format book.
02067  
02068   The Flex outline fragment is described by
02069     c1: (x0, y0) = c3: (x0, yshrink(y0)) or (xshrink(x0), y0)
02070      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
02071      "  (x2, y2) - reference point
02072     c2: (x0, y0) = c4: (x0, yshrink(y0)) or (xshrink(x0), y0)
02073      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
02074      "  (x2, y2) =  "  (x2, y2), rightmost endpoint
02075     c3: (x0, y0) - control point, 1st Bezier curve
02076      "  (x1, y1) - control point,      -"-
02077      "  (x2, y2) - end point,          -"-
02078     c4: (x0, y0) - control point, 2nd Bezier curve
02079      "  (x1, y1) - control point,      -"-
02080      "  (x2, y2) - end point,          -"-
02081     ep: (epY, epX) - final endpoint (should be same as c4: (x2, y2))
02082     idmin - minimum Flex height (1/100 pixel) at which to render curves
02083 */
02084  
02085 #define dtransform(dxusr,dyusr,dxdev,dydev) { \
02086   register struct segment *point = Loc(CharSpace, dxusr, dyusr); \
02087   QueryLoc(point, IDENTITY, dxdev, dydev); \
02088   Destroy(point); \
02089 }
02090  
02091 #define itransform(xdev,ydev,xusr,yusr) { \
02092   register struct segment *point = Loc(IDENTITY, xdev, ydev); \
02093   QueryLoc(point, CharSpace, xusr, yusr); \
02094   Destroy(point); \
02095 }
02096  
02097 #define transform(xusr,yusr,xdev,ydev) dtransform(xusr,yusr,xdev,ydev)
02098  
02099 #define PaintType (0)
02100  
02101 #define xshrink(x) ((x - c4x2) * shrink +c4x2)
02102 #define yshrink(y) ((y - c4y2) * shrink +c4y2)
02103  
02104 #define PickCoords(flag) \
02105   if (flag) { /* Pick "shrunk" coordinates */ \
02106     x0 = c1x0; y0 = c1y0; \
02107     x1 = c1x1; y1 = c1y1; \
02108     x2 = c1x2; y2 = c1y2; \
02109     x3 = c2x0; y3 = c2y0; \
02110     x4 = c2x1; y4 = c2y1; \
02111     x5 = c2x2; y5 = c2y2; \
02112   } else { /* Pick original coordinates */ \
02113     x0 = c3x0; y0 = c3y0; \
02114     x1 = c3x1; y1 = c3y1; \
02115     x2 = c3x2; y2 = c3y2; \
02116     x3 = c4x0; y3 = c4y0; \
02117     x4 = c4x1; y4 = c4y1; \
02118     x5 = c4x2; y5 = c4y2; \
02119   }
02120  
02121 /* FlxProc() = OtherSubrs[0]; Main part of Flex          */
02122 /*   Calling sequence: 'idmin epX epY 3 0 callothersubr' */
02123 /*   Computes Flex values, and renders the Flex path,    */
02124 /*   and returns (leaves) ending coordinates on stack    */
02125 static void FlxProc(c1x2, c1y2, c3x0, c3y0, c3x1, c3y1, c3x2, c3y2,
02126              c4x0, c4y0, c4x1, c4y1, c4x2, c4y2, epY, epX, idmin)
02127   DOUBLE c1x2, c1y2;
02128   DOUBLE c3x0, c3y0, c3x1, c3y1, c3x2, c3y2;
02129   DOUBLE c4x0, c4y0, c4x1, c4y1, c4x2, c4y2;
02130   DOUBLE epX, epY;
02131   int idmin;
02132 {
02133   DOUBLE dmin;
02134   DOUBLE c1x0, c1y0, c1x1, c1y1;
02135   DOUBLE c2x0, c2y0, c2x1, c2y1, c2x2, c2y2;
02136   char yflag;
02137   DOUBLE x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5;
02138   DOUBLE cxx, cyx, cxy, cyy; /* Transformation matrix */
02139   int flipXY;
02140   DOUBLE x, y;
02141   DOUBLE erosion = 1; /* Device parameter */
02142     /* Erosion may have different value specified in 'internaldict' */
02143   DOUBLE shrink;
02144   DOUBLE dX, dY;
02145   char erode;
02146   DOUBLE eShift;
02147   DOUBLE cx, cy;
02148   DOUBLE ex, ey;
02149 
02150 
02151   /* Our PPOINT list now contains 7 moveto commands which
02152      are about to be consumed by the Flex mechanism. --> Remove these
02153      seven elements (their values already reside on the PSFakeStack!)
02154      and approriately restore the current accumulated position. */
02155   numppoints -= 7;
02156   currx = ppoints[numppoints-1].x;
02157   curry = ppoints[numppoints-1].y;
02158  
02159   if (ProcessHints) {
02160     dmin = TYPE1_ABS(idmin) / 100.0; /* Minimum Flex height in pixels */
02161  
02162     c2x2 = c4x2; c2y2 = c4y2; /* Point c2 = c4 */
02163  
02164     yflag = FABS(c1y2 - c3y2) > FABS(c1x2 - c3x2); /* Flex horizontal? */
02165  
02166     QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
02167  
02168     if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
02169       flipXY = -1; /* Char on side */
02170     else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
02171       flipXY = 1; /* Char upright */
02172     else
02173       flipXY = 0; /* Char at angle */
02174  
02175     if (yflag) { /* Flex horizontal */
02176       if (flipXY == 0 || c3y2 == c4y2) { /* Char at angle or Flex height = 0 */
02177         PickCoords(FALSE); /* Pick original control points */
02178       } else {
02179         shrink = FABS((c1y2 - c4y2) / (c3y2 - c4y2)); /* Slope */
02180  
02181         c1x0 = c3x0; c1y0 = yshrink(c3y0);
02182         c1x1 = c3x1; c1y1 = yshrink(c3y1);
02183         c2x0 = c4x0; c2y0 = yshrink(c4y0);
02184         c2x1 = c4x1; c2y1 = yshrink(c4y1);
02185  
02186         dtransform(0.0, ROUND(c3y2-c1y2), &x, &y); /* Flex height in pixels */
02187         dY = FABS((flipXY == 1) ? y : x);
02188         PickCoords(dY < dmin); /* If Flex small, pick 'shrunk' control points */
02189  
02190         if (FABS(y2 - c1y2) > 0.001) { /* Flex 'non-zero'? */
02191           transform(c1x2, c1y2, &x, &y);
02192  
02193           if (flipXY == 1) {
02194             cx = x; cy = y;
02195           } else {
02196             cx = y; cy = x;
02197           }
02198  
02199           dtransform(0.0, ROUND(y2-c1y2), &x, &y);
02200           dY = (flipXY == 1) ? y : x;
02201           if (ROUND(dY) != 0)
02202             dY = ROUND(dY);
02203           else
02204             dY = (dY < 0) ? -1 : 1;
02205  
02206           erode = PaintType != 2 && erosion >= 0.5;
02207           if (erode)
02208             cy -= 0.5;
02209           ey = cy + dY;
02210           ey = CEIL(ey) - ey + FLOOR(ey);
02211           if (erode)
02212             ey += 0.5;
02213  
02214           if (flipXY == 1) {
02215             itransform(cx, ey, &x, &y);
02216           } else {
02217             itransform(ey, cx, &x, &y);
02218           }
02219  
02220           eShift = y - y2;
02221           y1 += eShift;
02222           y2 += eShift;
02223           y3 += eShift;
02224         }
02225       }
02226     } else { /* Flex vertical */
02227       if (flipXY == 0 || c3x2 == c4x2) { /* Char at angle or Flex height = 0 */
02228         PickCoords(FALSE); /* Pick original control points */
02229       } else {
02230         shrink = FABS((c1x2 - c4x2) / (c3x2 - c4x2)); /* Slope */
02231  
02232         c1x0 = xshrink(c3x0); c1y0 = c3y0;
02233         c1x1 = xshrink(c3x1); c1y1 = c3y1;
02234         c2x0 = xshrink(c4x0); c2y0 = c4y0;
02235         c2x1 = xshrink(c4x1); c2y1 = c4y1;
02236  
02237         dtransform(ROUND(c3x2 - c1x2), 0.0, &x, &y); /* Flex height in pixels */
02238         dX = FABS((flipXY == -1) ? y : x);
02239         PickCoords(dX < dmin); /* If Flex small, pick 'shrunk' control points */
02240  
02241         if (FABS(x2 - c1x2) > 0.001) {
02242           transform(c1x2, c1y2, &x, &y);
02243           if (flipXY == -1) {
02244             cx = y; cy = x;
02245           } else {
02246             cx = x; cy = y;
02247           }
02248  
02249           dtransform(ROUND(x2-c1x2), 0.0, &x, &y);
02250           dX = (flipXY == -1) ? y : x;
02251           if (ROUND(dX) != 0)
02252             dX = ROUND(dX);
02253           else
02254             dX = (dX < 0) ? -1 : 1;
02255  
02256           erode = PaintType != 2 && erosion >= 0.5;
02257           if (erode)
02258             cx -= 0.5;
02259           ex = cx + dX;
02260           ex = CEIL(ex) - ex + FLOOR(ex);
02261           if (erode)
02262             ex += 0.5;
02263  
02264           if (flipXY == -1) {
02265             itransform(cy, ex, &x, &y);
02266           } else {
02267             itransform(ex, cy, &x, &y);
02268           }
02269  
02270           eShift = x - x2;
02271           x1 += eShift;
02272           x2 += eShift;
02273           x3 += eShift;
02274         }
02275       }
02276     }
02277  
02278     if (x2 == x5 || y2 == y5) {
02279       RLineTo( x5 - currx, y5 - curry); \
02280     } else {
02281       RRCurveTo( x0 - currx, y0 - curry,
02282                x1 - x0, y1 - y0,
02283                x2 - x1,
02284                y2 - y1); 
02285       RRCurveTo( x3 - currx, y3 - curry,
02286                x4 - x3, y4 - y3,
02287                x5 - x4, y5 - y4); 
02288     }
02289   } else { /* ProcessHints is off */
02290     PickCoords(FALSE); /* Pick original control points */
02291     RRCurveTo( x0 - currx, y0 - curry,
02292               x1 - x0, y1 - y0,
02293               x2 - x1,
02294               y2 - y1); 
02295     RRCurveTo( x3 - currx, y3 - curry,
02296               x4 - x3, y4 - y3,
02297               x5 - x4, y5 - y4); 
02298   }
02299  
02300   PSFakePush(epY);
02301   PSFakePush(epX);
02302 }
02303  
02304 /* FlxProc1() = OtherSubrs[1]; Part of Flex            */
02305 /*   Calling sequence: '0 1 callothersubr'             */
02306 /*   Saves and clears path, then restores currentpoint */
02307 static void FlxProc1()
02308 {
02309   /* Since we are now building the path point list,
02310      there's nothing to do here! */
02311   return;
02312 }
02313  
02314 /* FlxProc2() = OtherSubrs[2]; Part of Flex */
02315 /*   Calling sequence: '0 2 callothersubr'  */
02316 /*   Returns currentpoint on stack          */
02317 static void FlxProc2()
02318 {
02319   struct segment *CurrentPoint;
02320   DOUBLE CurrentX, CurrentY;
02321   
02322   /* Push CurrentPoint on fake PostScript stack */
02323   PSFakePush( ppoints[numppoints-1].x);
02324   PSFakePush( ppoints[numppoints-1].y);
02325 }
02326  
02327 /* HintReplace() = OtherSubrs[3]; Hint Replacement            */
02328 /*   Calling sequence: 'subr# 1 3 callothersubr pop callsubr' */
02329 /*   Reinitializes stem hint structure                        */
02330 static void HintReplace()
02331 {
02332   /* Effectively retire the current stems, but keep them around for */
02333   /* revhint use in case we are in a stem when we replace hints. */
02334   currstartstem = numstems;
02335  
02336   /* 'subr#' is left on PostScript stack (for 'pop callsubr') */
02337 }
02338  
02339 /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
02340 /* Make calls on the PostScript interpreter (or call equivalent C code) */
02341 /* NOTE: The n arguments have been pushed on the fake PostScript stack */
02342 static int CallOtherSubr(othersubrno)
02343   int othersubrno;
02344 {
02345   IfTrace1((FontDebug), "CallOtherSubr %d\n", othersubrno);
02346   
02347   switch(othersubrno) {
02348   case 0: /* OtherSubrs[0]; Main part of Flex */
02349     if (PSFakeTop < 16) Error0i("CallOtherSubr: PSFakeStack low");
02350     ClearPSFakeStack();
02351     FlxProc(
02352            PSFakeStack[0],  PSFakeStack[1],  PSFakeStack[2],  PSFakeStack[3],
02353            PSFakeStack[4],  PSFakeStack[5],  PSFakeStack[6],  PSFakeStack[7],
02354            PSFakeStack[8],  PSFakeStack[9],  PSFakeStack[10], PSFakeStack[11],
02355            PSFakeStack[12], PSFakeStack[13], PSFakeStack[14], PSFakeStack[15],
02356            (int) PSFakeStack[16]
02357            );
02358     break;
02359   case 1: /* OtherSubrs[1]; Part of Flex */
02360     FlxProc1();
02361     break;
02362   case 2: /* OtherSubrs[2]; Part of Flex */
02363     FlxProc2();
02364     break;
02365   case 3: /* OtherSubrs[3]; Hint Replacement */
02366     HintReplace();
02367     break;
02368   case 12: /* OtherSubrs[12]: Counter Control Hinting */
02369     /* We do nothing, because later OtherSubrs[13] will
02370        remove data from the PS-stack */
02371     break;
02372   case 13:
02373     /* We pop all elements from PSFakeStack. They may either have been left
02374        from previous calls to OtherSubr #12 or have been pushed for this
02375        call */
02376     ClearPSFakeStack();
02377   break;
02378   default: { /* call OtherSubrs[4] or higher if PostScript is present */
02379     
02380   }
02381   }
02382   return(0);
02383 }
02384  
02385 /* |- x y SETCURRENTPOINT |- */
02386 /* Sets the current point to (x,y) in absolute */
02387 /* character space coordinates without per- */
02388 /* forming a CharString MOVETO command */
02389 static int SetCurrentPoint(x, y)
02390   DOUBLE x, y;
02391 {
02392   IfTrace2((FontDebug), "SetCurrentPoint %f %f\n", x, y);
02393  
02394   currx = x;
02395   curry = y;
02396   return(0);
02397 }
02398  
02399 /* The Type1Char routine for use by PostScript. */
02400 /************************************************/
02401 struct xobject *Type1Char(psfont *env, struct XYspace *S,
02402                        psobj *charstrP, psobj *subrsP,
02403                        psobj *osubrsP,
02404                        struct blues_struct *bluesP,
02405                        int *modeP, char *charname,
02406                        float strokewidth)
02407 {
02408   int Code;
02409   long i = 0;
02410   
02411   double xp, yp;
02412 #ifdef DUMPDEBUGPATH
02413   char fnbuf[128];
02414 #endif
02415   struct segment* p;
02416 
02417   /* Reset point list */
02418   ppoints          = NULL;
02419   numppoints       = 0;
02420   numppointchunks  = 0;
02421 
02422   /* disable hinting for stroking */
02423   if ( strokewidth != 0.0f )
02424     ProcessHints = 0;
02425   
02426   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 )
02427     ProcessHints = 0;
02428 
02429   path    = NULL;
02430   apath   = NULL;
02431   errflag = FALSE;
02432  
02433   /* Make parameters available to all Type1 routines */
02434   currentchar=charname;
02435   Environment = (char *)env;
02436   CharSpace = S; /* used when creating path elements */
02437   CharStringP = charstrP;
02438   SubrsP = subrsP;
02439   OtherSubrsP = osubrsP;
02440   ModeP = modeP;
02441  
02442   blues = bluesP;
02443  
02444   QuerySpace( S, &scxx, &scyx, &scxy, &scyy); /* Transformation matrix */
02445   p = ILoc( S, 1, 0);
02446   QueryLoc(p, IDENTITY, &xp, &yp);
02447   up = FABS(xp);
02448  
02449   size = scxx * 1000.0;
02450 #ifdef DUMPDEBUGPATH
02451   sprintf( fnbuf, "t1dump_%s_%f.eps", charname, size); 
02452   psfile = fopen( fnbuf, "wb");
02453   if ( psfile != NULL ) {
02454     PSDumpProlog( psfile);
02455     fprintf( psfile, "T1LibDict begin\n\ngsave\n%f t1SetupSize\nt1PreparePage\n", size);
02456   }
02457   
02458 #endif
02459 
02460   /* compute the alignment zones */
02461   SetRasterFlags();
02462   ComputeAlignmentZones();
02463   StartDecrypt();
02464   ClearStack();
02465   ClearPSFakeStack();
02466   ClearCallStack();
02467   InitStems();
02468 
02469   /* reset variables */
02470   currx = curry = 0.0;
02471   hcurrx = hcurry = 0.0;
02472   escapementX = escapementY = 0;
02473   sidebearingX = sidebearingY = 0;
02474   accentoffsetX = accentoffsetY = 0;
02475   wsoffsetX = wsoffsetY = 0;           /* No shift to preserve whitspace. */
02476   wsset = 0;                           /* wsoffsetX,Y haven't been set yet. */
02477 
02478   
02479   for (;;) {
02480     if (!DoRead(&Code)) break;
02481     Decode(Code);
02482     if (errflag) break;
02483   }
02484 
02485   /* We now have a point list in absolute charspace coordinates. Adjust
02486      non-hinted points to suppress hinting artifacts and generate path. */
02487   for ( i=0; i<numppoints; i++ ) {
02488     if ( ppoints[i].type == PPOINT_BEZIER_D)
02489       adjustBezier( i);
02490   }
02491   /* Create path elements */
02492 #if defined(DUMPDEBUGPATH) & defined(DUMPDEBUGPATHBOTH)
02493   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 0 ) {
02494     /* For this special debug case, we generate both a filled and a stroked
02495        path!. */
02496       createStrokePath( strokewidth, SUBPATH_CLOSED);
02497       createFillPath();
02498   }
02499   else if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 ) {
02500     /* PaintType = 1 indicates stroked font. If strokewidth is 0.0f,
02501        we stroke using the font's internal strokewidth. Otherwise, the
02502        user supplied value is used. */
02503     if ( strokewidth != 0.0f )
02504       createStrokePath( strokewidth, SUBPATH_OPEN);
02505     else
02506       createStrokePath( env->fontInfoP[STROKEWIDTH].value.data.real, SUBPATH_OPEN);
02507   }
02508 #else
02509   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 0 ) {
02510     /* PaintType = 0 indicates filled font. Hence, a strokewidth value
02511        other than 0.0 indicates the character is to be stroked instead
02512        of to be filled. */
02513     if ( strokewidth != 0.0f )
02514       createStrokePath( strokewidth, SUBPATH_CLOSED);
02515     else
02516       createFillPath();
02517   }
02518   else if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 ) {
02519     /* PaintType = 1 indicates stroked font. If strokewidth is 0.0f,
02520        we stroke using the font's internal strokewidth. Otherwise, the
02521        user supplied value is used. */
02522     if ( strokewidth != 0.0f )
02523       createStrokePath( strokewidth, SUBPATH_OPEN);
02524     else
02525       createStrokePath( env->fontInfoP[STROKEWIDTH].value.data.real, SUBPATH_OPEN);
02526   }
02527 #endif
02528   
02529   /* check and handle accented char */
02530   if ( apath != NULL ) {
02531     path = Join( apath, path);
02532   }
02533   
02534   /* Report a possible error: */
02535   *modeP=errflag;
02536   
02537   /* Clean up if an error has occurred */
02538   if (errflag) {
02539     if (path != NULL) {
02540       Destroy(path); /* Reclaim storage */
02541       path = NULL;   /* Indicate that character could not be built */
02542     }
02543   }
02544  
02545 #ifdef DUMPDEBUGPATH
02546   if ( psfile != NULL ) {
02547     PSDumpEpilog( psfile);
02548     fclose( psfile);
02549     psfile = 0;
02550   }
02551 #endif
02552 
02553   /* Cleanup ppoints */
02554   if ( ppoints != NULL ) {
02555     free( ppoints);
02556     ppoints = NULL;
02557     numppoints = 0;
02558   }
02559   
02560   return((struct xobject *) path);
02561 }
02562  
02563 
02564 /* We add a function to implement underlining in Type1-Routines. */
02565 struct xobject *Type1Line(psfont *env, struct XYspace *S,
02566                        float line_position,
02567                        float line_thickness,
02568                        float line_length,
02569                        float strokewidth)
02570 {
02571 
02572   /* Reset point list */
02573   ppoints          = NULL;
02574   numppoints       = 0;
02575   numppointchunks  = 0;
02576   
02577   path    = NULL;
02578   apath   = NULL;
02579   errflag = FALSE;
02580 
02581   
02582   /* Make parameters available to all Type1 routines */
02583   Environment = (char *)env;
02584   CharSpace = S; /* used when creating path elements */
02585 
02586   currx = curry = 0;
02587   escapementX = escapementY = 0;
02588   sidebearingX = sidebearingY = 0;
02589   /* We have to initialize the stems-structures because
02590      the lineto commands refer to them! */
02591   SetRasterFlags();
02592   InitStems();
02593   
02594   /* Draw Character */
02595   Sbw(-1.0*line_length, 0.0, 0.0, 0.0);
02596   RMoveTo( 0.0,  (double) (line_position+0.5*line_thickness));
02597   RLineTo( 0.0, (double) -line_thickness);
02598   RLineTo( (double) line_length, 0.0);
02599   RLineTo( 0.0, (double) line_thickness);
02600   DoClosePath();  
02601   EndChar();
02602 
02603   /* Create path */
02604   if ( strokewidth != 0.0f )
02605     createStrokePath( strokewidth, SUBPATH_CLOSED);
02606   else
02607     createFillPath();
02608   
02609   /* Cleanup ppoints */
02610   if ( ppoints != NULL ) {
02611     free( ppoints);
02612     ppoints = NULL;
02613   }
02614 
02615   return((struct xobject *)path);
02616   
02617 }
02618 
02619 
02620 /* Adjust the points of a given cubic Bezier Spline so that the
02621    geometric relation of points B and C to A and D remain
02622    qualitatively the same. This reduces hinting artifacts
02623    at low resolutions.
02624 */
02625 static void adjustBezier( long pindex)
02626 {
02627   double deltax  = 0.0;      /* x distance between point A and D */
02628   double deltay  = 0.0;      /* y distance between point A and D */
02629   double adeltax = 0.0;      /* x distance between hinted point A and D */
02630   double adeltay = 0.0;      /* y distance between hinted point A and D */
02631 
02632   deltax  = ppoints[pindex].x - ppoints[pindex-3].x;
02633   deltay  = ppoints[pindex].y - ppoints[pindex-3].y;
02634   adeltax = ppoints[pindex].ax - ppoints[pindex-3].ax;
02635   adeltay = ppoints[pindex].ay - ppoints[pindex-3].ay;
02636 
02637   if ( deltax == 0 || deltay == 0 )
02638     return;
02639   
02640   ppoints[pindex-1].ax = ppoints[pindex-3].ax +
02641     (adeltax / deltax * (ppoints[pindex-1].x - ppoints[pindex-3].x));
02642   ppoints[pindex-1].ay = ppoints[pindex-3].ay +
02643     (adeltay / deltay * (ppoints[pindex-1].y - ppoints[pindex-3].y));
02644   ppoints[pindex-2].ax = ppoints[pindex-3].ax +
02645     (adeltax / deltax * (ppoints[pindex-2].x - ppoints[pindex-3].x));
02646   ppoints[pindex-2].ay = ppoints[pindex-3].ay +
02647     (adeltay / deltay * (ppoints[pindex-2].y - ppoints[pindex-3].y));
02648 
02649   return;
02650   
02651 }
02652 
02653 
02654 
02655 /* This function actually generates path segments. It is called
02656    after all hinting and adjustments have been performed.
02657 */
02658 static void handleCurrentSegment( long pindex)
02659 {
02660   struct segment* B;
02661   struct segment* C;
02662   struct segment* D;
02663   struct segment* tmpseg;
02664   double dx1;
02665   double dy1;
02666   double dx2;
02667   double dy2;
02668   double dx3;
02669   double dy3;
02670   double adx1;
02671   double ady1;
02672   double adx2;
02673   double ady2;
02674   double adx3;
02675   double ady3;
02676   
02677 
02678   /* handle the different segment types in a switch-statement */
02679   switch ( ppoints[pindex].type ) {
02680 
02681   case PPOINT_MOVE:
02682     /* handle a move segment */
02683     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
02684     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
02685     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
02686     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
02687     
02688 #ifdef DUMPDEBUGPATH
02689     if ( psfile != NULL )
02690       fprintf( psfile, "%f %f t1rmoveto\n", dx1*up, dy1*up);
02691 #endif
02692     if ( ProcessHints ) {
02693       IfTrace2((FontDebug), "RMoveTo(h) %f %f\n", adx1, ady1);
02694       B = Loc(CharSpace, adx1, ady1);
02695 #ifdef DUMPDEBUGPATH
02696       if ( psfile != NULL ) {
02697        fprintf( psfile, "%f %f t1hintedrmoveto\n", adx1*up, ady1*up);
02698       }
02699 #endif
02700     }
02701     else {
02702       IfTrace2((FontDebug), "RMoveTo %f %f\n", dx1, dy1);
02703       B = Loc(CharSpace, dx1, dy1);
02704     }
02705     path = Join(path, B);
02706     break;
02707 
02708 
02709   case PPOINT_LINE:
02710     /* handle a line segment */
02711     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
02712     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
02713     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
02714     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
02715     
02716 #ifdef DUMPDEBUGPATH
02717     if ( psfile != NULL )
02718       fprintf( psfile, "%f %f t1rlineto\n", dx1*up, dy1*up);
02719 #endif
02720     if ( ProcessHints ) {
02721       IfTrace2((FontDebug), "RLineTo(h) %f %f\n", adx1, ady1);
02722       B = Loc(CharSpace, adx1, ady1);
02723 #ifdef DUMPDEBUGPATH
02724       if ( psfile != NULL ) {
02725        fprintf( psfile, "%f %f t1hintedrlineto\n", adx1*up, ady1*up);
02726       }
02727 #endif
02728     }
02729     else {
02730       IfTrace2((FontDebug), "RLineTo %f %f\n", dx1, dy1);
02731       B = Loc(CharSpace, dx1, dy1);
02732     }
02733     path = Join(path, Line(B));
02734     break;
02735 
02736 
02737   case PPOINT_BEZIER_B:
02738     /* handle a bezier segment (given by this and the following points) */
02739     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
02740     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
02741     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
02742     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
02743     dx2  = ppoints[pindex+1].x - ppoints[pindex].x;
02744     dy2  = ppoints[pindex+1].y - ppoints[pindex].y;
02745     adx2 = ppoints[pindex+1].ax - ppoints[pindex].ax;
02746     ady2 = ppoints[pindex+1].ay - ppoints[pindex].ay;
02747     dx3  = ppoints[pindex+2].x - ppoints[pindex+1].x;
02748     dy3  = ppoints[pindex+2].y - ppoints[pindex+1].y;
02749     adx3 = ppoints[pindex+2].ax - ppoints[pindex+1].ax;
02750     ady3 = ppoints[pindex+2].ay - ppoints[pindex+1].ay;
02751 
02752 
02753 #ifdef DUMPDEBUGPATH
02754     if ( psfile != NULL )
02755       fprintf( psfile, "%f %f %f %f %f %f t1rrcurveto\n",
02756               dx1*up, dy1*up,
02757               dx2*up, dy2*up,
02758               dx3*up, dy3*up);
02759 #endif
02760     if (ProcessHints) {
02761       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
02762               adx1, ady1, adx2, ady2);
02763       IfTrace2((FontDebug), "%f %f\n", adx3, ady3);
02764       B = Loc(CharSpace, adx1, ady1);
02765       C = Loc(CharSpace, adx2, ady2);
02766       D = Loc(CharSpace, adx3, ady3);
02767 #ifdef DUMPDEBUGPATH
02768       if ( psfile != NULL ) {
02769        fprintf( psfile, "%f %f %f %f %f %f t1hintedrrcurveto\n",
02770                adx1*up, ady1*up,
02771                adx2*up, ady2*up,
02772                adx3*up, ady3*up);
02773       }
02774 #endif
02775     }
02776     else {
02777       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
02778               dx1, dy1, dx2, dy2);
02779       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
02780       B = Loc(CharSpace, dx1, dy1);
02781       C = Loc(CharSpace, dx2, dy2);
02782       D = Loc(CharSpace, dx3, dy3);
02783     }
02784     
02785     /* Since XIMAGER is not completely relative, */
02786     /* we need to add up the delta values */
02787     C = Join(C, Dup(B));
02788     D = Join(D, Dup(C));
02789     path = Join(path, Bezier(B, C, D));
02790     break;
02791 
02792 
02793   case PPOINT_SBW:
02794 #ifdef DUMPDEBUGPATH
02795   if ( psfile != NULL )
02796     fprintf( psfile, "%f %f %f %f t1sbw\n",
02797             ppoints[pindex].x*up, ppoints[pindex].y*up,   /* sidebearings */
02798             ppoints[pindex].ax*up, ppoints[pindex].ay*up  /* escapements  */
02799             );
02800 #endif
02801     path = Join(path, Loc(CharSpace, ppoints[pindex].x, ppoints[pindex].y));
02802     break;
02803     
02804     
02805   case PPOINT_CLOSEPATH:
02806     IfTrace0((FontDebug), "DoClosePath\n");
02807 #ifdef DUMPDEBUGPATH
02808     if ( psfile != NULL ) {
02809       fprintf( psfile, "t1closepath\n");
02810       if ( ProcessHints )
02811        fprintf( psfile, "t1hintedclosepath\n");
02812     }
02813 #endif
02814     
02815     tmpseg = Phantom(path);
02816     path = ClosePath(path);
02817     path = Join(Snap(path), tmpseg);
02818     break;
02819     
02820     
02821   case PPOINT_ENDCHAR:
02822     /* Perform a Closepath just in case the command was left out */
02823     path = ClosePath(path);
02824     
02825     /* Set character width / escapement. It is stored in the vars for
02826        hinted coordinates. */
02827     path = Join(Snap(path), Loc(CharSpace, ppoints[pindex].ax, ppoints[pindex].ay));
02828     
02829 #ifdef DUMPDEBUGPATH
02830     if ( psfile != NULL )
02831       fputs( "t1FinishPage\ngrestore\n", psfile);
02832 #endif
02833     break;
02834     
02835 
02836   case PPOINT_SEAC:
02837     /* return to origin of accent */
02838     apath = Snap(path);
02839     /* reset path to NULL */
02840     path  = NULL;
02841     break;
02842     
02843     
02844   default:
02845     /* We must be at the beginning of the path, that is, there is
02846        nothing to do! */
02847     ;
02848   }
02849 
02850   return;
02851   
02852 }
02853 
02854 
02855 #ifdef DUMPDEBUGPATH
02856 static void PSDumpProlog( FILE* fp)
02857 {
02858 #include "t1chardump"
02859 }
02860 
02861 
02862 static void PSDumpEpilog( FILE* fp)
02863 {
02864   fputs( "\nend\n", fp);
02865 }
02866 
02867 #endif /* #ifdef DUMPDEBUGPATH */
02868 
02869 
02870 
02871 /* Take the accumulated path point list and construct the path that is
02872    to be filled. */
02873 static void createFillPath( void)
02874 {
02875   long i;
02876   
02877   for ( i=0; i<numppoints; i++ ) {
02878     handleCurrentSegment( i);
02879   }
02880   return;
02881 }
02882 
02883 
02884 /* Take the accumulated path point list and construct a path so that the
02885    character appears as a stroked outline, where the virtual pen has a diameter
02886    of strokewidth (measured in big points [bp]). This function works analogously
02887    to PostScripts charpath operator. */
02888 static void createStrokePath( double strokewidth, int subpathclosed)
02889 {
02890   long pindex   = 0;
02891   long startind = 0;
02892   long stopind  = 0;
02893 
02894 
02895   /* For each subpath in the path list (some sub path is closed!), we construct 
02896      one path interior and one path exterior to the path under consideration in
02897      a way, that the resulting thick curve has a thickness of strokewidth. */
02898 
02899   if ( subpathclosed == 0 ) {
02900     /* We have a stroked font */
02901     /* loop through all subpaths */
02902     while ( pindex < numppoints ) {
02903       /* First, handle any non-subpath commands. */
02904       if ( handleNonSubPathSegments( pindex) != 0 ) {
02905 #ifdef DEBUG_OUTLINE_SURROUNDING
02906        fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02907                ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02908 #endif
02909        ++pindex;
02910        continue;
02911       }
02912       
02913       if ( ppoints[pindex].type == PPOINT_LINE ||
02914           ppoints[pindex].type == PPOINT_BEZIER_B ) {
02915        if ( ppoints[pindex-1].type == PPOINT_MOVE ) {
02916          /* A line or curve segment following a move segment indicates a
02917             new subpath. */
02918          startind = pindex - 1;
02919          while ( (pindex < numppoints) &&
02920                 (ppoints[pindex].type != PPOINT_CLOSEPATH) &&
02921                 (ppoints[pindex].type != PPOINT_MOVE) && 
02922                 (ppoints[pindex].type != PPOINT_ENDCHAR) 
02923                 ) {
02924 #ifdef DEBUG_OUTLINE_SURROUNDING
02925            fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02926                  ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02927 #endif
02928            ++pindex;
02929          }
02930          if ( (ppoints[pindex].type == PPOINT_ENDCHAR) ||  /* for outline fonts */
02931               (ppoints[pindex].type == PPOINT_MOVE)          /* for stroked fonts */
02932               ) {
02933 #ifdef DEBUG_OUTLINE_SURROUNDING
02934            fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02935                  ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02936 #endif
02937            stopind = --pindex;
02938 #ifdef DEBUG_OUTLINE_SURROUNDING
02939            fprintf( stderr, "Found subpath from index %ld to %ld inclusively\n", startind, stopind);
02940 #endif
02941            /* We have found a subpath defined by the path points indexed by
02942               the interval from startind to stopind. */
02943            createClosedStrokeSubPath( startind, stopind, strokewidth, subpathclosed);
02944          }
02945        }
02946       }
02947       ++pindex;
02948     }
02949   }
02950   else {
02951     /* We have a filled font */
02952     /* loop through all subpaths */
02953     while ( pindex < numppoints ) {
02954       /* First, handle any non-subpath commands. */
02955       if ( handleNonSubPathSegments( pindex) != 0 ) {
02956 #ifdef DEBUG_OUTLINE_SURROUNDING
02957        fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02958               ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02959 #endif
02960        ++pindex;
02961        continue;
02962       }
02963       
02964       if ( ppoints[pindex].type == PPOINT_LINE ||
02965           ppoints[pindex].type == PPOINT_BEZIER_B ) {
02966        if ( ppoints[pindex-1].type == PPOINT_MOVE ) {
02967          /* A line or curve segment following a move segment indicates a
02968             new subpath. */
02969          startind = --pindex;
02970          while ( (pindex < numppoints) &&
02971                 (ppoints[pindex].type != PPOINT_CLOSEPATH) 
02972                 ) {
02973 #ifdef DEBUG_OUTLINE_SURROUNDING
02974            fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02975                  ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02976 #endif
02977            ++pindex;
02978          }
02979          if ( ppoints[pindex].type == PPOINT_CLOSEPATH ) {
02980 #ifdef DEBUG_OUTLINE_SURROUNDING
02981            fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
02982                  ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
02983 #endif
02984            stopind = pindex;
02985 #ifdef DEBUG_OUTLINE_SURROUNDING
02986            fprintf( stderr, "Found subpath from index %ld to %ld inclusively\n", startind, stopind);
02987 #endif
02988            /* We have found a subpath defined by the path points indexed by
02989               the interval from startind to stopind. */
02990            createClosedStrokeSubPath( startind, stopind, strokewidth, subpathclosed);
02991          }
02992        }
02993       }
02994       ++pindex;
02995     }
02996   }
02997   
02998   return;
02999   
03000 }
03001 
03002 
03003 
03004 /* Create two closed paths that surround the the current subpath of the
03005    charpath in a centered fashion. */
03006 static void createClosedStrokeSubPath( long startind, long stopind,
03007                                    double strokewidth, int subpathclosed)
03008 {
03009   long i;
03010   long inext;
03011   long iprev;
03012   long ip = 0;
03013   long in = 0;
03014   long segstartind;
03015   long segendind;
03016   
03017   long lastind     = 0; /* Index of last point whose location is different from first
03018                         point. After this point there may follow an explicit line-
03019                         or curveto to the starting point and also the ClosePath-point
03020                         may be and usually is identical to the starting point. */
03021 
03022   double dx1;
03023   double dy1;
03024   double dx2;
03025   double dy2;
03026   double dx3;
03027   double dy3;
03028   
03029   struct segment* B;
03030   struct segment* C;
03031   struct segment* D;
03032   struct segment* tmpseg;
03033 
03034   int type;
03035   
03036 
03037   /* The ClosePath operator is somewhat problematic, because it adds a point
03038      to the defining points of a path, without actually having a distance to
03039      the previous or the next point. This causes problems with the distance
03040      handling. As a remedy, we check whether ClosePath is located at the first
03041      point or the last point of the path. In the latter case, ClosePath causes
03042      an additional line segment. */
03043   if ( (ppoints[stopind].x == ppoints[startind].x) &&
03044        (ppoints[stopind].y == ppoints[startind].y)
03045        ) {
03046     closepathatfirst = 1;
03047   }
03048   else {
03049     closepathatfirst = 0;
03050   }
03051   
03052   
03053   /* For each path point in the list, we have to define a set of points, to the
03054      left and to the right of the current curve. The positions of these
03055      new points depend on the coordinates of the previous path point, the current
03056      path and the next path point. */
03057 
03058   /* For the computations, the distance from the start and end points of curves
03059      and lines to the neighbouring points is required. We start by calculating
03060      these and by filling in the path point entries dist2prev and dist2next for
03061      the respective points. */
03062   lastind = computeDistances( startind, stopind, subpathclosed);
03063 
03064 #ifdef DEBUG_OUTLINE_SURROUNDING
03065   fprintf( stderr, "startind=%ld, lastind=%ld, stopind=%ld\n", startind, lastind, stopind);
03066 #endif
03067 
03068   /********************************************************************************
03069    ********************************************************************************
03070    ***** 
03071    ***** Path point transformation
03072    ***** 
03073    ********************************************************************************
03074    ********************************************************************************/
03075 
03076   /* Next we step through the path points of the current subpath and compute the 
03077      points' transformations. From these newly computed points,
03078      the path is constructed. */
03079   for ( i=startind; i<=lastind; ) {
03080     /* Be aware of cycling! */
03081     if ( i == startind ) {
03082       iprev = lastind;
03083       inext = i + 1;
03084     }
03085     else if ( i == lastind ) {
03086       iprev = i - 1;
03087       inext = startind;
03088     }
03089     else {
03090       iprev = i - 1;
03091       inext = i + 1;
03092     }
03093     
03094     
03095     switch ( ppoints[i].type ) {
03096     case PPOINT_MOVE:
03097       /* The first segment always is of type PPOINT_MOVE. It is defined by the first,
03098         the second and the last point. */
03099       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
03100 
03101       /* Compute one point which results from prolongating the linked segment and
03102         and computing the intersection. The result values are stored in dxres,
03103         dyres. */
03104       if ( subpathclosed == 0 ) {
03105        /* open subpath --> stroked font */
03106        if ( i == startind ) {
03107          intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
03108        }
03109        else if ( i == lastind ) {
03110          intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
03111        }
03112        else {
03113          intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03114        }
03115       }
03116       else {
03117        intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03118       }
03119 
03120 #ifdef DEBUG_OUTLINE_SURROUNDING
03121       fprintf( stderr, "PP %ld (%s): (%f,%f), shape=%s;\n", i, pptypes[ppoints[i].type], 
03122               ppoints[i].x, ppoints[i].y, ppshapes[ppoints[i].shape]);
03123       fprintf( stderr, "                     Right: prev (%f,%f); next (%f,%f)\n",
03124               ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
03125               ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr);
03126       fprintf( stderr, "                     Left:  prev (%f,%f); next (%f,%f)\n",
03127               ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
03128               ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr);
03129       fprintf( stderr, "                     Res:  Right (%f,%f); Left (%f,%f)\n\n",
03130               ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir,
03131               ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
03132 #endif
03133       
03134       break;
03135 
03136       
03137     case PPOINT_LINE:
03138       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
03139       if ( subpathclosed == 0 ) {
03140        /* open subpath --> stroked font */
03141        if ( i == startind )
03142          intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
03143        else if ( i == lastind )
03144          intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
03145        else
03146          intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03147       }
03148       else {
03149        intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03150       }
03151       
03152 #ifdef DEBUG_OUTLINE_SURROUNDING
03153       fprintf( stderr, "PP %ld (%s): (%f,%f), shape=%s; \n",
03154               i, pptypes[ppoints[i].type], ppoints[i].x, ppoints[i].y,
03155               ppshapes[ppoints[i].shape]);
03156       fprintf( stderr, "                     Right: prev (%f,%f); next (%f,%f)\n",
03157               ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
03158               ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr);
03159       fprintf( stderr, "                     Left:  prev (%f,%f); next (%f,%f)\n",
03160               ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
03161               ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr);
03162       fprintf( stderr, "                     Res:  Right (%f,%f); Left (%f,%f)\n\n",
03163               ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir,
03164               ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
03165 #endif
03166 
03167       break;
03168 
03169       
03170     case PPOINT_BEZIER_B:
03171       break;
03172 
03173     case PPOINT_BEZIER_C:
03174       break;
03175 
03176     case PPOINT_BEZIER_D:
03177       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
03178       if ( subpathclosed == 0 ) {
03179        /* open subpath --> stroked font */
03180        if ( i == startind )
03181          intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
03182        else if ( i == lastind )
03183          intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
03184        else
03185          intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03186       }
03187       else {
03188        intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
03189       }
03190       
03191 #ifdef DEBUG_OUTLINE_SURROUNDING
03192       fprintf( stderr, "PP %ld (%s): (%f,%f), shape=%s;\n",
03193               i, pptypes[ppoints[i].type], ppoints[i].x, ppoints[i].y,
03194               ppshapes[ppoints[i].shape]);
03195       fprintf( stderr, "                     Right: prev (%f,%f); next (%f,%f)\n",
03196               ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
03197               ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr);
03198       fprintf( stderr, "                     Left:  prev (%f,%f); next (%f,%f)\n",
03199               ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
03200               ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr);
03201       fprintf( stderr, "                     Res:  Right (%f,%f); Left (%f,%f)\n\n",
03202               ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir,
03203               ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
03204 #endif
03205       
03206       /* transform the preceding two offCurve points */
03207       transformOffCurvePathPoint( strokewidth, i-2);
03208 
03209       break;
03210       
03211     case PPOINT_CLOSEPATH:
03212 
03213       break;
03214       
03215     default:
03216       break;
03217     }
03218     ++i;
03219   }
03220 
03221   /* copy the shift values from starting point to ending points that
03222      have not yet been handled */
03223   for ( ; i<=stopind; i++) {
03224     ppoints[i].dxpr      = ppoints[startind].dxpr;
03225     ppoints[i].dypr      = ppoints[startind].dypr;
03226     ppoints[i].dxnr      = ppoints[startind].dxnr;
03227     ppoints[i].dynr      = ppoints[startind].dynr;
03228     ppoints[i].dxir      = ppoints[startind].dxir;
03229     ppoints[i].dyir      = ppoints[startind].dyir;
03230     ppoints[i].dist2prev = ppoints[startind].dist2prev;
03231     ppoints[i].dist2next = ppoints[startind].dist2next;
03232     if ( ppoints[i].type == PPOINT_BEZIER_D ) {
03233       transformOffCurvePathPoint( strokewidth, i-2);
03234     }
03235     ppoints[i].shape     = ppoints[startind].shape;
03236   }
03237   
03238 
03239   
03240   /* We now have computed the resulting shift values for each path point of the current
03241      subpath's right path. The values for the left path follow by negation.
03242      The path is still to be build!
03243   */
03244 
03245   /********************************************************************************
03246    ********************************************************************************
03247    ***** 
03248    ***** Construction of right path
03249    ***** 
03250    ********************************************************************************
03251    ********************************************************************************/
03252 
03253   /* The leading move segment is treated separately. First check from which
03254      point the leading Moveto was called. This is safe even in cases where
03255      multiple moveto appear in a series. */
03256   i = startind;
03257   while ( ppoints[i].type == PPOINT_MOVE )
03258     --i;
03259   dx1  = ppoints[startind].x - (ppoints[i].x);
03260   dy1  = ppoints[startind].y - (ppoints[i].y);
03261   /* If the first node in the subpath is not concave, we may directly jump
03262      to the intersection right path point. Otherwise, we remain at the onCurve
03263      point because later, prolongation will happen. */
03264   if ( ppoints[startind].shape != CURVE_CONCAVE ) {
03265     dx1  += ppoints[startind].dxir;
03266     dy1  += ppoints[startind].dyir;
03267   }
03268   
03269 #ifdef DUMPDEBUGPATH
03270   if ( psfile != NULL )
03271     fprintf( psfile, "%f %f t1srmoveto\n", dx1*up, dy1*up);
03272 #endif
03273   B = Loc(CharSpace, dx1, dy1);
03274   path = Join(path, B);
03275   
03276   
03277   /* Now, handle the remaining path in a loop */
03278   for ( i=startind+1; i<=stopind; ) {
03279     switch ( ppoints[i].type ) {
03280     case PPOINT_LINE:
03281       /* handle a line segment */
03282       
03283       /* 1. Check and handle starting node */
03284       linkNode( i-1, PATH_START, PATH_RIGHT);
03285 
03286       /* 2. Draw ideal isolated line segment */
03287 #ifdef DEBUG_OUTLINE_SURROUNDING
03288        fprintf( stderr, "RP:  Line from point %ld to %ld\n", i-1, i);
03289 #endif
03290       dx1  = ppoints[i].x + ppoints[i].dxpr - (ppoints[i-1].x + ppoints[i-1].dxnr);
03291       dy1  = ppoints[i].y + ppoints[i].dypr - (ppoints[i-1].y + ppoints[i-1].dynr);
03292 #ifdef DUMPDEBUGPATH
03293       if ( psfile != NULL )
03294        fprintf( psfile, "%f %f t1srlineto\n", dx1*up, dy1*up);
03295 #endif
03296       B = Loc(CharSpace, dx1, dy1);
03297       path = Join(path, Line(B));
03298 
03299       /* 3. Check and handle ending node */
03300       linkNode( i, PATH_END, PATH_RIGHT);
03301 
03302       break;
03303 
03304     case PPOINT_BEZIER_B:
03305       break;
03306     case PPOINT_BEZIER_C:
03307       break;
03308     case PPOINT_BEZIER_D:
03309       /* handle a bezier segment (given by this and the previous 3 path points)! */
03310 
03311       /* 1. Check and handle starting node */
03312       linkNode( i-3, PATH_START, PATH_RIGHT);
03313 
03314       /* 2. Draw curve based on ideal point locations */
03315 #ifdef DEBUG_OUTLINE_SURROUNDING
03316       fprintf( stderr, "RP:  Curve from PP %ld to PP %ld to PP %ld to PP %ld\n",
03317               i-3, i-2, i-1, i);
03318 #endif
03319       dx1  = ppoints[i-2].x + ppoints[i-2].dxir - (ppoints[i-3].x + ppoints[i-3].dxnr);
03320       dy1  = ppoints[i-2].y + ppoints[i-2].dyir - (ppoints[i-3].y + ppoints[i-3].dynr);
03321       dx2  = ppoints[i-1].x + ppoints[i-1].dxir - (ppoints[i-2].x + ppoints[i-2].dxir);
03322       dy2  = ppoints[i-1].y + ppoints[i-1].dyir - (ppoints[i-2].y + ppoints[i-2].dyir);
03323       dx3  = ppoints[i].x +   ppoints[i].dxpr   - (ppoints[i-1].x + ppoints[i-1].dxir);
03324       dy3  = ppoints[i].y +   ppoints[i].dypr   - (ppoints[i-1].y + ppoints[i-1].dyir);
03325       
03326 #ifdef DUMPDEBUGPATH
03327       if ( psfile != NULL )
03328        fprintf( psfile, "%f %f %f %f %f %f t1srrcurveto\n",
03329                dx1*up, dy1*up,
03330                dx2*up, dy2*up,
03331                dx3*up, dy3*up);
03332 #endif
03333       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
03334               dx1, dy1, dx2, dy2);
03335       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
03336       B = Loc(CharSpace, dx1, dy1);
03337       C = Loc(CharSpace, dx2, dy2);
03338       D = Loc(CharSpace, dx3, dy3);
03339     
03340       C = Join(C, Dup(B));
03341       D = Join(D, Dup(C));
03342       path = Join(path, Bezier(B, C, D));
03343 
03344       /* 3. Check and handle starting node */
03345       linkNode( i, PATH_END, PATH_RIGHT);
03346 
03347       break;
03348 
03349       
03350     case PPOINT_CLOSEPATH:
03351 #ifdef DEBUG_OUTLINE_SURROUNDING
03352       fprintf( stderr, "RP:  ClosePath command ignored\n");
03353 #endif
03354       
03355       break;
03356 
03357     default:
03358       break;
03359       
03360     }
03361     ++i;
03362   }
03363 
03364   /********************************************************************************
03365    ********************************************************************************
03366    ***** 
03367    ***** Close right path
03368    ***** 
03369    ********************************************************************************
03370    ********************************************************************************/
03371 
03372   if ( subpathclosed != 0 ) {
03373     /* We are stroking an outline font to be filled */ 
03374     if ( closepathatfirst == 0 ) {
03375       /* Because of the concavity issue, we may not simply use
03376         the closepath operator here. Instead we have to manage a possible
03377         prolongation manually if the closepath would cause a line segment. */
03378 
03379       /* 1. Check and handle starting node */
03380       linkNode( lastind, PATH_START, PATH_RIGHT);
03381 
03382       /* 2. Draw ideal isolated line segment */
03383       dx1  = ppoints[startind].x + ppoints[startind].dxpr - (ppoints[lastind].x + ppoints[lastind].dxnr);
03384       dy1  = ppoints[startind].y + ppoints[startind].dypr - (ppoints[lastind].y + ppoints[lastind].dynr);
03385 #ifdef DUMPDEBUGPATH
03386       if ( psfile != NULL )
03387        fprintf( psfile, "%f %f t1srlineto\n", dx1*up, dy1*up);
03388 #endif
03389       B = Loc(CharSpace, dx1, dy1);
03390       path = Join(path, Line(B));
03391 
03392       /* 3. Check and handle ending node */
03393       linkNode( startind, PATH_END, PATH_RIGHT);
03394 
03395     } /* if ( closepathatfirst == 0) */
03396 
03397     /* Now close path formally. Anyhow, this won't cause a line segment! */
03398 #ifdef DUMPDEBUGPATH
03399     if ( psfile != NULL ) {
03400       fprintf( psfile, "t1sclosepath\n");
03401     }
03402 #endif
03403     tmpseg = Phantom(path);
03404     path = ClosePath(path);
03405     path = Join(Snap(path), tmpseg);
03406 
03407     
03408     /********************************************************************************
03409      ********************************************************************************
03410      ***** 
03411      ***** Stepping to beginning of left path
03412      ***** 
03413      ********************************************************************************
03414      ********************************************************************************/
03415     
03416     /* If curve is concave at the subpath's starting point, the location is onCurve
03417        and the left path is convex, there. Conversely, if the curve is convex, the
03418        location is at the right intersection point and the left path will be concave
03419        so that the initial location must be onCurve. Hence, for both cases, we have
03420        to translate back once the intersection shift.
03421 
03422        If the curve is straight at the starting point, we directly jump from the right
03423        intersection point ot he left intersection point.
03424     */
03425     if ( (ppoints[startind].shape == CURVE_CONCAVE) ||
03426         (ppoints[startind].shape == CURVE_CONVEX) ) { 
03427       dx1 = - ppoints[startind].dxir;
03428       dy1 = - ppoints[startind].dyir;
03429     }
03430     else {
03431       dx1 = - 2.0 * ppoints[startind].dxir;
03432       dy1 = - 2.0 * ppoints[startind].dyir;
03433     }
03434 
03435 #ifdef DUMPDEBUGPATH
03436     if ( psfile != NULL )
03437       fprintf( psfile, "%f %f t1srmoveto\n", dx1*up, dy1*up);
03438 #endif
03439     B = Loc(CharSpace, dx1, dy1);
03440     path = Join(path, B);
03441   } /* if ( subpathclose != 0 */
03442   else {
03443     /* We have a stroked font. In this case, a line segment has to be drawn */
03444     if ( (ppoints[stopind].shape == CURVE_CONCAVE) ||
03445         (ppoints[stopind].shape == CURVE_CONVEX) ) { 
03446       dx1 = - ppoints[stopind].dxir;
03447       dy1 = - ppoints[stopind].dyir;
03448     }
03449     else {
03450       dx1 = - 2.0 * ppoints[stopind].dxir;
03451       dy1 = - 2.0 * ppoints[stopind].dyir;
03452     }
03453 
03454 #ifdef DUMPDEBUGPATH
03455     if ( psfile != NULL )
03456       fprintf( psfile, "%f %f t1srlineto\n", dx1*up, dy1*up);
03457 #endif
03458     B = Loc(CharSpace, dx1, dy1);
03459     path = Join(path, Line(B));
03460     
03461   }
03462   
03463   
03464   /********************************************************************************
03465    ********************************************************************************
03466    ***** 
03467    ***** Construction of left path
03468    ***** 
03469    ********************************************************************************
03470    ********************************************************************************/
03471   
03472   /* Create left path. This is somewhat more complicated, because the
03473      order/direction has to be exchanged. */
03474 #ifdef DEBUG_OUTLINE_SURROUNDING
03475   fprintf( stderr, "Constructing LeftPath: stopind=%ld, lastind=%ld, closepathatfirst=%d\n",
03476           stopind, lastind, closepathatfirst);
03477 #endif
03478   for ( i=stopind; i>=startind; ) {
03479     if ( subpathclosed != 0 ) {
03480       /* closed subpath --> filled font */
03481       if ( i == stopind ) {
03482        ip   = startind;
03483        if ( (closepathatfirst != 0) )
03484          type = ppoints[ip].type;
03485        else
03486          type = PPOINT_NONE;
03487       }
03488       else if ( i == startind ) {
03489        ip   = startind + 1;
03490        type = ppoints[ip].type;
03491       }
03492       else {
03493        ip   = i + 1;
03494        type = ppoints[ip].type;
03495       }
03496     }
03497     else {
03498       /* open subpath --> stroked font */
03499       type   = ppoints[i].type;
03500       in     = i - 1;
03501     }
03502 
03503     /* Step through path in inverted direction.
03504        Note: - ip is the index of the starting point, i the index of the
03505                ending point of the current segment.
03506              - If the path point is flagged "concave", then this reverts into
03507                "convex" in the left path and vice versa!
03508             - there is an index shift of 1 between closed and open subpaths.
03509     */
03510     switch ( type ) {
03511     case PPOINT_MOVE:
03512       
03513       break;
03514       
03515     case PPOINT_LINE:
03516 
03517       /* handle a line segment */
03518       if ( subpathclosed != 0 ) {
03519        segendind    = i;
03520        segstartind  = ip;
03521       }
03522       else {
03523        segstartind  = i;
03524        segendind    = in;
03525       }
03526       
03527       /* 1. Check and handle starting node */
03528       linkNode( segstartind, PATH_START, PATH_LEFT);
03529 
03530       /* 2. Draw ideal isolated line segment */
03531 #ifdef DEBUG_OUTLINE_SURROUNDING
03532       fprintf( stderr, "LP:  Line from point %ld to %ld\n", segstartind, segendind);
03533 #endif
03534       dx1  = ppoints[segendind].x - ppoints[segendind].dxnr -
03535        (ppoints[segstartind].x - ppoints[segstartind].dxpr);
03536       dy1  = ppoints[segendind].y - ppoints[segendind].dynr -
03537        (ppoints[segstartind].y - ppoints[segstartind].dypr);
03538 #ifdef DUMPDEBUGPATH
03539       if ( psfile != NULL )
03540        fprintf( psfile, "%f %f t1srlineto\n", dx1*up, dy1*up);
03541 #endif
03542       B = Loc(CharSpace, dx1, dy1);
03543       path = Join(path, Line(B));
03544 
03545       /* 3. Check and handle ending node */
03546       linkNode( segendind, PATH_END, PATH_LEFT);
03547 
03548       break;
03549 
03550       
03551     case PPOINT_BEZIER_B:
03552       break;
03553 
03554     case PPOINT_BEZIER_C:
03555       break;
03556 
03557     case PPOINT_BEZIER_D:
03558       /* handle a bezier segment (given by this and the previous 3 path points)!
03559         For bezier segments, we may not simply apply the intersection of previous
03560         and next candidate because that would damage the curve's layout. Instead,
03561         in cases where the candidate produced by intersection is not identical to
03562         the ideal point, we prolongate and link the distance with a line segment.
03563       */
03564 
03565       /* 1. Check and handle starting node */
03566       linkNode( ip, PATH_START, PATH_LEFT);
03567 
03568       /* 2. Draw ideal curve segment */
03569 #ifdef DEBUG_OUTLINE_SURROUNDING
03570       fprintf( stderr, "LP:  Curve from PP %ld to PP %ld to PP %ld to PP %ld\n",
03571               ip, ip-1, ip-2, ip-3);
03572 #endif
03573       /* Use ideal point locations for curve at starting and ending point: */
03574       dx1  = ppoints[ip-1].x - ppoints[ip-1].dxir - (ppoints[ip].x   - ppoints[ip].dxpr);
03575       dy1  = ppoints[ip-1].y - ppoints[ip-1].dyir - (ppoints[ip].y   - ppoints[ip].dypr);
03576       dx2  = ppoints[ip-2].x - ppoints[ip-2].dxir - (ppoints[ip-1].x - ppoints[ip-1].dxir);
03577       dy2  = ppoints[ip-2].y - ppoints[ip-2].dyir - (ppoints[ip-1].y - ppoints[ip-1].dyir);
03578       dx3  = ppoints[ip-3].x - ppoints[ip-3].dxnr - (ppoints[ip-2].x - ppoints[ip-2].dxir);
03579       dy3  = ppoints[ip-3].y - ppoints[ip-3].dynr - (ppoints[ip-2].y - ppoints[ip-2].dyir);
03580 
03581 #ifdef DUMPDEBUGPATH
03582       if ( psfile != NULL )
03583        fprintf( psfile, "%f %f %f %f %f %f t1srrcurveto\n",
03584                dx1*up, dy1*up,
03585                dx2*up, dy2*up,
03586                dx3*up, dy3*up);
03587 #endif
03588       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
03589               dx1, dy1, dx2, dy2);
03590       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
03591       B = Loc(CharSpace, dx1, dy1);
03592       C = Loc(CharSpace, dx2, dy2);
03593       D = Loc(CharSpace, dx3, dy3);
03594     
03595       C = Join(C, Dup(B));
03596       D = Join(D, Dup(C));
03597       path = Join(path, Bezier(B, C, D));
03598 
03599       /* 3. Check and handle ending node */
03600       linkNode( ip-3, PATH_END, PATH_LEFT);
03601 
03602       break;
03603 
03604       
03605     case PPOINT_CLOSEPATH:
03606       
03607       /* Handle a ClosePath segment, if it had
03608         caused a line segment. Hence, actually, we handle
03609         a line segment here. */
03610       if ( closepathatfirst == 1 ) {
03611        /* ignore this command */
03612        break;
03613       }
03614 
03615       /* 1. Check and handle starting node */
03616       linkNode( startind, PATH_START, PATH_LEFT);
03617 
03618       /* 2. Draw ideal isolated line segment */
03619 #ifdef DEBUG_OUTLINE_SURROUNDING
03620       fprintf( stderr, "LP:  Inverted ClosePath from point %ld to %ld\n", startind, lastind);
03621 #endif
03622       if ( subpathclosed != 0 ) {
03623        dx1  = ppoints[lastind].x - ppoints[lastind].dxnr - (ppoints[startind].x - ppoints[startind].dxpr);
03624        dy1  = ppoints[lastind].y - ppoints[lastind].dynr - (ppoints[startind].y - ppoints[startind].dypr);
03625       }
03626       else {
03627        dx1  = -(ppoints[i].x - ppoints[i].dxnr - (ppoints[ip].x - ppoints[ip].dxpr));
03628        dy1  = -(ppoints[i].y - ppoints[i].dynr - (ppoints[ip].y - ppoints[ip].dypr));
03629       }
03630       
03631 #ifdef DUMPDEBUGPATH
03632       if ( psfile != NULL )
03633        fprintf( psfile, "%f %f t1srlineto\n", dx1*up, dy1*up);
03634 #endif
03635       B = Loc(CharSpace, dx1, dy1);
03636       path = Join(path, Line(B));
03637 
03638       /* 3. Check and handle ending node */
03639       linkNode( lastind, PATH_END, PATH_LEFT);
03640       
03641       break;
03642       
03643     default:
03644       break;
03645       
03646     }
03647     --i;
03648   }
03649 
03650 #ifdef DUMPDEBUGPATH
03651   if ( psfile != NULL ) {
03652     fprintf( psfile, "t1sclosepath\n");
03653   }
03654 #endif
03655   tmpseg = Phantom(path);
03656   path = ClosePath(path);
03657   path = Join(Snap(path), tmpseg);
03658   
03659   
03660   /********************************************************************************
03661    ********************************************************************************
03662    ***** 
03663    ***** Move to final position
03664    ***** 
03665    ********************************************************************************
03666    ********************************************************************************/
03667   
03668   /* Step to back to starting point of this subpath. If closepathatfirst==0, the
03669      final closepath caused a line segment. In this case, we first have to step
03670      back that segment and proceed from this point. */
03671   if ( ppoints[startind].shape == CURVE_CONVEX ) {
03672     /* In this case, left path is concave and the current location is at
03673        the onCurve point */
03674     dx1  = 0.0;
03675     dy1  = 0.0;
03676   }
03677   else {
03678     /* OK, it seems to be the intersection point */
03679     dx1  = ppoints[startind].dxir;
03680     dy1  = ppoints[startind].dyir;
03681   }
03682   /* We are now onCurve. If necessary step to the point where the closepath
03683      appeared. */
03684   if ( closepathatfirst == 0 ) {
03685     dx1 += ppoints[lastind].x - ppoints[startind].x;
03686     dy1 += ppoints[lastind].y - ppoints[startind].y;
03687   }
03688 
03689   
03690 #ifdef DUMPDEBUGPATH
03691   if ( psfile != NULL )
03692     fprintf( psfile, "%f %f t1srmoveto\n", dx1*up, dy1*up);
03693 #endif
03694   B = Loc(CharSpace, dx1, dy1);
03695   path = Join(path, B);
03696 
03697   return;
03698   
03699 }
03700 
03701 
03702 
03703 /* Compute distance from OnCurve-points to their neighbouring points, fill in
03704    the respective entries dist2prev and dist2next in the ppoints[] structures
03705    and return the index of the last point in the current subpath which has
03706    a location different from the starting point of the subpath. */
03707 static long computeDistances( long startind, long stopind, int subpathclosed)
03708 {
03709   long   lastind       = 0;
03710   double dx            = 0.0;
03711   double dy            = 0.0;
03712   long   i             = 0;
03713   int    neighboured   = 0;
03714 
03715 
03716   /* Handle first point as a special case */
03717   /* distance to previous point. First, get index of previous point. */
03718   lastind = stopind;
03719 
03720   if ( subpathclosed != 0 ) {
03721     if ( (ppoints[startind].x == ppoints[stopind].x) &&
03722         (ppoints[startind].y == ppoints[stopind].y) ) {
03723       while ( (ppoints[lastind].x == ppoints[stopind].x) &&
03724              (ppoints[lastind].y == ppoints[stopind].y))
03725        --lastind;
03726     }
03727     else {
03728       lastind = stopind - 1;
03729     }
03730   }
03731   
03732 #ifdef DEBUG_OUTLINE_SURROUNDING
03733   fprintf( stderr,
03734           "computeDistance(): startind=%ld stopind=%ld, lastind=%ld, start.x=%f, start.y=%f, last.x=%f, last.y=%f\n",
03735           startind, stopind, lastind, ppoints[startind].x, ppoints[startind].y,
03736           ppoints[lastind].x, ppoints[lastind].y);
03737 #endif
03738   
03739   dx = ppoints[startind].x - ppoints[lastind].x;
03740   dy = ppoints[startind].y - ppoints[lastind].y;
03741   ppoints[startind].dist2prev = sqrt( dx*dx + dy*dy );
03742   
03743   /* distance to next point */
03744   dx = ppoints[startind+1].x - ppoints[startind].x;
03745   dy = ppoints[startind+1].y - ppoints[startind].y;
03746   ppoints[startind].dist2next = sqrt( dx*dx + dy*dy );
03747   
03748 #ifdef DEBUG_OUTLINE_SURROUNDING
03749   fprintf( stderr,
03750           "Pre: Distance at point %ld: Prev=%f Next=%f\n",
03751           startind, ppoints[startind].dist2prev, ppoints[startind].dist2next);
03752 #endif
03753   
03754   for ( i = startind+1; i < lastind; i++ ) {
03755     if ( (ppoints[i].type == PPOINT_MOVE) ||
03756         (ppoints[i].type == PPOINT_LINE) ||
03757         (ppoints[i].type == PPOINT_BEZIER_D) ) {
03758       if ( neighboured ) {
03759        ppoints[i].dist2prev = ppoints[i-1].dist2next;
03760       }
03761       else {
03762        /* distance to previous point */
03763        dx = ppoints[i].x - ppoints[i-1].x;
03764        dy = ppoints[i].y - ppoints[i-1].y;
03765        /* Take care of degenerated curves */
03766        if ( (dx == 0.0) && (dy == 0.0) ) {
03767          dx = ppoints[i].x - ppoints[i-2].x;
03768          dy = ppoints[i].y - ppoints[i-2].y;
03769          if ( (dx == 0.0) && (dy == 0.0) ) {
03770            dx = ppoints[i].x - ppoints[i-3].x;
03771            dy = ppoints[i].y - ppoints[i-3].y;
03772          }
03773        }
03774        ppoints[i].dist2prev = sqrt( dx*dx + dy*dy );
03775       }
03776       /* distance to next point */
03777       dx = ppoints[i+1].x - ppoints[i].x;
03778       dy = ppoints[i+1].y - ppoints[i].y;
03779       /* Take care of degenerated curves */
03780       if ( (dx == 0.0) && (dy == 0.0) ) {
03781        dx = ppoints[i+2].x - ppoints[i].x;
03782        dy = ppoints[i+2].y - ppoints[i].y;
03783        if ( (dx == 0.0) && (dy == 0.0) ) {
03784          dx = ppoints[i+3].x - ppoints[i].x;
03785          dy = ppoints[i+3].y - ppoints[i].y;
03786        }
03787       }
03788       ppoints[i].dist2next = sqrt( dx*dx + dy*dy );
03789       neighboured = 1;
03790 #ifdef DEBUG_OUTLINE_SURROUNDING
03791       fprintf( stderr, "     Distance at point %ld: Prev=%f Next=%f\n",
03792               i, ppoints[i].dist2prev, ppoints[i].dist2next);
03793 #endif
03794     }
03795     else {
03796       neighboured = 0;
03797     }
03798     
03799   }
03800   /* We still have to handle the last point */
03801   /* distance to previous point */
03802   dx = ppoints[lastind].x - ppoints[lastind-1].x;
03803   dy = ppoints[lastind].y - ppoints[lastind-1].y;
03804   /* Take care of degenerated curves */
03805   if ( (dx == 0.0) && (dy == 0.0) ) {
03806     dx = ppoints[lastind].x - ppoints[lastind-2].x;
03807     dy = ppoints[lastind].y - ppoints[lastind-2].y;
03808     if ( (dx == 0.0) && (dy == 0.0) ) {
03809       dx = ppoints[lastind].x - ppoints[lastind-3].x;
03810       dy = ppoints[lastind].y - ppoints[lastind-3].y;
03811     }
03812   }
03813   ppoints[lastind].dist2prev = sqrt( dx*dx + dy*dy );
03814   /* distance to next point */
03815   ppoints[lastind].dist2next = ppoints[startind].dist2prev;
03816 #ifdef DEBUG_OUTLINE_SURROUNDING
03817   fprintf( stderr, "End: Distance at point %ld: Prev=%f Next=%f\n",
03818           lastind, ppoints[lastind].dist2prev, ppoints[lastind].dist2next);
03819 #endif
03820   
03821   return lastind;
03822   
03823 }
03824 
03825 
03826 
03827 /*
03828 
03829  */
03830 static long handleNonSubPathSegments( long pindex)
03831 {
03832 
03833   /* handle the different segment types in a switch-statement */
03834   switch ( ppoints[pindex].type ) {
03835 
03836   case PPOINT_SBW:
03837 #ifdef DUMPDEBUGPATH
03838   if ( psfile != NULL )
03839     fprintf( psfile, "%f %f %f %f t1sbw\n",
03840             ppoints[pindex].x*up, ppoints[pindex].y*up,   /* sidebearings */
03841             ppoints[pindex].ax*up, ppoints[pindex].ay*up  /* escapements  */
03842             );
03843 #endif
03844     path = Join(path, Loc(CharSpace, ppoints[pindex].x, ppoints[pindex].y));
03845     return 1;
03846     break;
03847     
03848 
03849   case PPOINT_ENDCHAR:
03850     /* Perform a Closepath just in case the command was left out */
03851     path = ClosePath(path);
03852     
03853     /* Set character width / escapement. It is stored in the vars for
03854        hinted coordinates. */
03855     path = Join(Snap(path), Loc(CharSpace, ppoints[pindex].ax, ppoints[pindex].ay));
03856     
03857 #ifdef DUMPDEBUGPATH
03858     if ( psfile != NULL )
03859       fputs( "t1FinishPage\n", psfile);
03860 #endif
03861     return 1;
03862     break;
03863     
03864 
03865   case PPOINT_SEAC:
03866     /* return to origin of accent */
03867     apath = Snap(path);
03868     /* reset path to NULL */
03869     path  = NULL;
03870     return 1;
03871     break;
03872     
03873     
03874   default:
03875     /* not handled, return 0! */
03876     ;
03877   }
03878   
03879   return 0;
03880   
03881 }
03882 
03883 
03884 
03885 /* Transform a path point according to the path's incoming angle, the path's
03886    outgoing angle and the parameter strokewidth. The computation is based on
03887    simple geometric considerations and makes use of the distance from the
03888    current point to the previous point and the next point respectively.
03889 
03890    Generally, each link to a path point induces its own candidate by simply
03891    widening the respective link orthogonally to strokewidth/2. This yields
03892    two displacement vectors (dx,dy) for the link from the previous point to the
03893    point under consideration (dxp, dyp) and and for the link from the current
03894    point to the next point (dxn, dyn).
03895 
03896    Later on, the two candidates are used to compute the resulting displacement
03897    as the intersection of the prolongated lines from before and behind the
03898    current point.
03899 
03900    Additionally, check whether the curve is concave or convex at this point.
03901    This is required for prolongation in the context of stroking.
03902 */
03903 static void transformOnCurvePathPoint( double strokewidth,
03904                                    long prevind, long currind, long nextind)
03905 {
03906   double distxp;
03907   double distyp;
03908   double distxn;
03909   double distyn;
03910   double det;
03911 
03912   /*
03913   distxp =  (ppoints[currind].y - ppoints[prevind].y);
03914   distyp = -(ppoints[currind].x - ppoints[prevind].x);
03915   distxn =  (ppoints[nextind].y - ppoints[currind].y);
03916   distyn = -(ppoints[nextind].x - ppoints[currind].x);
03917 
03918   ppoints[currind].dxpr = distxp * strokewidth * 0.5 / ppoints[currind].dist2prev;
03919   ppoints[currind].dypr = distyp * strokewidth * 0.5 / ppoints[currind].dist2prev;
03920 
03921   ppoints[currind].dxnr = distxn * strokewidth * 0.5 / ppoints[currind].dist2next;
03922   ppoints[currind].dynr = distyn * strokewidth * 0.5 / ppoints[currind].dist2next;
03923   */
03924   /* Note: When computing transformations of OnCurve points, we consider two
03925            special cases:
03926 
03927           1) The OnCurve beginning or end point is equal to the neighboring
03928              control point of a Bezier-Segment.
03929 
03930           2) This holds for beginning *and* end point. In this case the curve
03931              degenerates to a straight lines.
03932 
03933           Although this is deprecated by Adobe, fonts that use such constructions
03934           exist (e.g.m lower case 'n' of Univers 55). However, we do not care
03935           for segments that do not any escapement at all!
03936   */
03937   
03938   distxp =  (ppoints[currind].y - ppoints[prevind].y);
03939   distyp = -(ppoints[currind].x - ppoints[prevind].x);
03940   if ( (distxp == 0.0) && (distyp == 0.0) ) {
03941     distxp =  (ppoints[currind].y - ppoints[prevind-1].y);
03942     distyp = -(ppoints[currind].x - ppoints[prevind-1].x);
03943     if ( (distxp == 0.0) && (distyp == 0.0) ) {
03944       distxp =  (ppoints[currind].y - ppoints[prevind-2].y);
03945       distyp = -(ppoints[currind].x - ppoints[prevind-2].x);
03946     }
03947   }
03948   ppoints[currind].dxpr = distxp * strokewidth * 0.5 / ppoints[currind].dist2prev;
03949   ppoints[currind].dypr = distyp * strokewidth * 0.5 / ppoints[currind].dist2prev;
03950   
03951   distxn =  (ppoints[nextind].y - ppoints[currind].y);
03952   distyn = -(ppoints[nextind].x - ppoints[currind].x);
03953   if ( (distxn == 0.0) && (distyn == 0.0) ) {
03954     distxn =  (ppoints[nextind+1].y - ppoints[currind].y);
03955     distyn = -(ppoints[nextind+1].x - ppoints[currind].x);
03956     if ( (distxn == 0.0) && (distyn == 0.0) ) {
03957       distxn =  (ppoints[nextind+2].y - ppoints[currind].y);
03958       distyn = -(ppoints[nextind+2].x - ppoints[currind].x);
03959     }
03960   }
03961   ppoints[currind].dxnr = distxn * strokewidth * 0.5 / ppoints[currind].dist2next;
03962   ppoints[currind].dynr = distyn * strokewidth * 0.5 / ppoints[currind].dist2next;
03963   
03964   /* Consider determinant of the two tangent vectors at this node in order to
03965      decide whether the curve is convex or cancave at this point. */
03966   if ( (det = ((distxp * distyn) - (distxn * distyp))) < 0.0 ) {
03967     /* curve turns to the right */
03968     ppoints[currind].shape = CURVE_CONCAVE;
03969   }
03970   else if ( det > 0.0 ) {
03971     /* curve turns to the left */
03972     ppoints[currind].shape = CURVE_CONVEX;
03973   }
03974   else {
03975     /* curve is straight */
03976     ppoints[currind].shape = CURVE_STRAIGHT;
03977   }
03978   
03979   return;
03980 }
03981 
03982 
03983 /* Compute a displacement for offCurve points, that is, for Bezier B and C points.
03984    
03985    This computation is not as simple as it might appear at a first glance and,
03986    depending on the actual curve parameters and the parameter strokewidth, it might
03987    be necessary to subdivide the curve. My mathematical background is not actually
03988    reliable in this context but it seems that in particular the angle that the curve
03989    runs through is important in this context. Since the Adobe Type 1 recommendations
03990    on font design include a rule which states that curves' end points should be located
03991    at extreme values, and consequently, that the angle of a curve segment should not
03992    be larger than 90 degrees, I have decided not to implement curve subdivision. This
03993    might lead to some deviations if fonts do not adhere to the Adobe recommendations.
03994    Anyways, I have never seen such fonts.
03995 
03996    This function is called for Bezier_B points and computes displacements for the B
03997    and C points at once. Fortunately, index cycling cannot happen here.
03998 
03999    The new Bezier B' and C' points can be considered as four degrees of freedom and we have
04000    to find 4 equations to be able to compute them.
04001 
04002    1) We require the tangents slope at point A' to be identical to the slope at the
04003       point A of the ideally thin mathematical curve.
04004 
04005    2) The same holds for the tangents slope at point D' with respect to point D.
04006 
04007    3) We compute the following points
04008 
04009       P1:       Equally subdivides the line A - B
04010       P2:       Equally subdivides the line B - C
04011       P3:       Equally subdivides the line C - D
04012 
04013       P4:       Equally subdivides the line P1 - P2
04014       P5:       Equally subdivides the line P1 - P3
04015 
04016       P6:       Equally subdivides the line P4 - P5
04017 
04018       This latter point is part of the curve and, moreover, the line P4 - P5 is
04019       tangent to the curve at that point.
04020       From this point, we compute a displacement for P6, orthogonally to the curve
04021       at that point and with length strokewidth/2. The resulting point is part of
04022       the delimiting path that makes up the thick curve.
04023 
04024    4) We require that the tangent's slope at P6' equals the tangents slope at P6.
04025 
04026    Then, under certain further constraints as mentioned above, we compute the points
04027    B' and C' making use of the points A' and D' which have been transformed as onCurve
04028    points. By definition, for offCurve points, there is only one candidate.
04029 
04030  */
04031 static void transformOffCurvePathPoint( double strokewidth, long currind)
04032 {
04033   double diameter;
04034   double dx;
04035   double dy;
04036   
04037   /* points defining the curve */
04038   double pax;
04039   double pay;
04040   double pbx;
04041   double pby;
04042   double pcx;
04043   double pcy;
04044   double pdx;
04045   double pdy;
04046   
04047   /* auxiliary points from iterative Bezier construction */
04048   double p1x;
04049   double p1y;
04050   double p2x;
04051   double p2y;
04052   double p3x;
04053   double p3y;
04054   double p4x;
04055   double p4y;
04056   double p5x;
04057   double p5y;
04058   double p6x;
04059   double p6y;
04060   
04061   /* already displaced / shifted onCurve points and the ones we are going
04062      to compute. */
04063   double paxs;
04064   double pays;
04065   double pbxs;
04066   double pbys;
04067   double pcxs;
04068   double pcys;
04069   double pdxs;
04070   double pdys;
04071 
04072   /* The normal vector on the curve at t=1/2 */
04073   double nabs;
04074   double nx;
04075   double ny;
04076 
04077   /* some variables for computations at point B' */
04078   double bloc1x;         
04079   double bloc1y;
04080   double bdir1x;
04081   double bdir1y;
04082   double bloc2x;
04083   double bloc2y;
04084   double bdir2x;
04085   double bdir2y;
04086   double bdet;
04087   double binvdet;
04088   double binvdir1x;
04089   double binvdir1y; 
04090   double binvdir2x;
04091   double binvdir2y; 
04092   double bmu;
04093   double bnu; 
04094 
04095   /* some variables for computations at point C' */
04096   double cloc1x;         
04097   double cloc1y;
04098   double cdir1x;
04099   double cdir1y;
04100   double cloc2x;
04101   double cloc2y;
04102   double cdir2x;
04103   double cdir2y;
04104   double cdet;
04105   double cinvdet;
04106   double cinvdir1x;
04107   double cinvdir1y; 
04108   double cinvdir2x;
04109   double cinvdir2y; 
04110   double cmu;
04111   double cnu; 
04112   
04113   diameter = strokewidth * 0.5;
04114   
04115   pax = ppoints[currind-1].x;
04116   pay = ppoints[currind-1].y;
04117   pbx = ppoints[currind].x;
04118   pby = ppoints[currind].y;
04119   pcx = ppoints[currind+1].x;
04120   pcy = ppoints[currind+1].y;
04121   pdx = ppoints[currind+2].x;
04122   pdy = ppoints[currind+2].y;
04123   
04124   p1x = (pax + pbx) * 0.5;
04125   p1y = (pay + pby) * 0.5;
04126   p2x = (pbx + pcx) * 0.5;
04127   p2y = (pby + pcy) * 0.5;
04128   p3x = (pcx + pdx) * 0.5;
04129   p3y = (pcy + pdy) * 0.5;
04130   p4x = (p1x + p2x) * 0.5;
04131   p4y = (p1y + p2y) * 0.5;
04132   p5x = (p2x + p3x) * 0.5;
04133   p5y = (p2y + p3y) * 0.5;
04134   p6x = (p4x + p5x) * 0.5;
04135   p6y = (p4y + p5y) * 0.5;
04136 
04137   
04138   /* We start by computing the shift of the onCurve points. It is not possible
04139      to use  dxr / dyr of the ppoints-stucture entries. These values have been
04140      computed by intersection of both links to a path point. Here we need the
04141      ideal points of the thick isolated curve segment. We are aware that for
04142      Bezier splines, control point and OnCurve point might be identical! */
04143   dx   =   (ppoints[currind].y - ppoints[currind-1].y) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
04144   dy   = - (ppoints[currind].x - ppoints[currind-1].x) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
04145   if ( (dx == 0.0) && (dy == 0.0) ) {
04146     /* Bezier_A and Bezier_B are identical */
04147     dx   =   (ppoints[currind+1].y - ppoints[currind-1].y) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
04148     dy   = - (ppoints[currind+1].x - ppoints[currind-1].x) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
04149   }
04150   paxs = ppoints[currind-1].x + dx;
04151   pays = ppoints[currind-1].y + dy;
04152   dx   =   (ppoints[currind+2].y - ppoints[currind+1].y) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
04153   dy   = - (ppoints[currind+2].x - ppoints[currind+1].x) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
04154   if ( (dx == 0.0) && (dy == 0.0) ) {
04155     /* Bezier_C and Bezier_D are identical */
04156     dx   =   (ppoints[currind+2].y - ppoints[currind].y) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
04157     dy   = - (ppoints[currind+2].x - ppoints[currind].x) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
04158   }
04159   pdxs = ppoints[currind+2].x + dx;
04160   pdys = ppoints[currind+2].y + dy;
04161 
04162   /* Next, we compute the right side normal vector at the curve point t=1/2,
04163    that is, at P6. */
04164   nabs    = diameter / sqrt(((p5x - p4x) * (p5x - p4x)) + ((p5y - p4y) * (p5y - p4y)));
04165   nx      = (p5y - p4y) * nabs;
04166   ny      = (p4x - p5x) * nabs;
04167 
04168 #ifdef DEBUG_OUTLINE_SURROUNDING
04169   fprintf( stderr, "transformOffCurvePathPoint():\n");
04170   fprintf( stderr, "    A=(%f,%f), B=(%f,%f), C=(%f,%f), D=(%f,%f)\n",
04171           pax, pay, pbx, pby, pcx, pcy, pdx, pdy);
04172   fprintf( stderr, "%% DebugInfo: Curve from PP %ld ... PP %ld ... PP %ld ... PP %ld. StrokeWidth=%f.\n",
04173           currind-1, currind, currind+1, currind+2, strokewidth);
04174   fprintf( stderr, "/xa %f def\n/ya %f def\n/xb %f def\n/yb %f def\n/xc %f def\n/yc %f def\n/xd %f def\n/yd %f def\n",
04175           pax, pay, pbx, pby, pcx, pcy, pdx, pdy);
04176   fprintf( stderr, "    As=(%f,%f), Ds=(%f,%f)\n",
04177           paxs, pays, pdxs, pdys);
04178   fprintf( stderr, "    p6=(%f,%f)\n", p6x, p6y);
04179   fprintf( stderr, "    nx=%f, ny=%f, nabs=%f\n", nx, ny, nabs);
04180   fprintf( stderr, "    p6s=(%f,%f)\n", p6x+nx, p6y+ny);
04181 #endif
04182 
04183   /* Compute two lines whose intersection will define point B' */
04184   bloc1x = (4.0 * (nx + p6x) - (2 * paxs) + pdxs) / 3.0;
04185   bloc1y = (4.0 * (ny + p6y) - (2 * pays) + pdys) / 3.0;
04186   bdir1x = pcx + pdx - pax - pbx;
04187   bdir1y = pcy + pdy - pay - pby;
04188   bloc2x = paxs;
04189   bloc2y = pays;
04190   bdir2x = pbx - pax;
04191   bdir2y = pby - pay;
04192   bdet   = (bdir2x * bdir1y) - (bdir2y * bdir1x);
04193   
04194 #ifdef DEBUG_OUTLINE_SURROUNDING
04195   fprintf( stderr, "    bloc1x=%f, bloc1y=%f, bloc2x,=%f bloc2y=%f\n",
04196           bloc1x, bloc1y, bloc2x, bloc2y);
04197   fprintf( stderr, "    bdir1x=%f, bdir1y=%f, bdir2x,=%f bdir2y=%f\n",
04198           bdir1x, bdir1y, bdir2x, bdir2y);
04199 #endif
04200 
04201   /* Switch if determinant is zero; we then actually have a straight line */
04202   if ( fabs(bdet) < 0.001 ) {
04203     pbxs   = pbx + nx;
04204     pbys   = pby + ny;
04205     bmu    = 0.0;
04206     bnu    = 0.0;
04207   }
04208   else {
04209     /* Calculate required part of inverse matrix */
04210     binvdet   =   1.0 / bdet;
04211     binvdir2x =   bdir1y * binvdet;
04212     binvdir2y = - bdir2y * binvdet; 
04213     binvdir1x = - bdir1x * binvdet;
04214     binvdir1y =   bdir2x * binvdet; 
04215 
04216     /* Calculate coefficient that describes intersection */
04217     bmu       =   (binvdir2x * (bloc1x - bloc2x)) + (binvdir1x * (bloc1y - bloc2y));
04218     bnu       =   (binvdir2y * (bloc1x - bloc2x)) + (binvdir1y * (bloc1y - bloc2y)); 
04219 
04220     /* Calculate B' */
04221     pbxs      =   bloc2x + (bmu * bdir2x);
04222     pbys      =   bloc2y + (bmu * bdir2y);
04223   }
04224 
04225   /* Compute two lines whose intersection will define point C' */
04226   cloc1x = (4.0 * (nx + p6x) - (2 * pdxs) + paxs) / 3.0;
04227   cloc1y = (4.0 * (ny + p6y) - (2 * pdys) + pays) / 3.0;
04228   cdir1x = bdir1x;
04229   cdir1y = bdir1y;
04230   cloc2x = pdxs;
04231   cloc2y = pdys;
04232   cdir2x = pcx - pdx;
04233   cdir2y = pcy - pdy;
04234   cdet   = (cdir2x * cdir1y) - (cdir2y * cdir1x);
04235 
04236 #ifdef DEBUG_OUTLINE_SURROUNDING
04237   fprintf( stderr, "    cloc1x=%f, cloc1y=%f, cloc2x,=%f cloc2y=%f\n",
04238           cloc1x, cloc1y, cloc2x, cloc2y);
04239   fprintf( stderr, "    cdir1x=%f, cdir1y=%f, cdir2x,=%f cdir2y=%f\n",
04240           cdir1x, cdir1y, cdir2x, cdir2y);
04241 #endif
04242 
04243   /* Switch if determinant is zero; we then actually have a straight line */
04244   if ( fabs( cdet) < 0.001 ) {
04245     pcxs   = pcx + nx;
04246     pcys   = pcy + ny;
04247     cmu    = 0.0;
04248     cnu    = 0.0;
04249   }
04250   else {
04251     /* Calculate required part of inverse matrix */
04252     cinvdet   =   1.0 / cdet;
04253     cinvdir2x =   cdir1y * cinvdet;
04254     cinvdir2y = - cdir2y * cinvdet; 
04255     cinvdir1x = - cdir1x * cinvdet;
04256     cinvdir1y =   cdir2x * cinvdet; 
04257 
04258     /* Calculate coefficient that describes intersection */
04259     cmu       =   (cinvdir2x * (cloc1x - cloc2x)) + (cinvdir1x * (cloc1y - cloc2y));
04260     cnu       =   (cinvdir2y * (cloc1x - cloc2x)) + (cinvdir1y * (cloc1y - cloc2y)); 
04261 
04262     /* Calculate C' */
04263     pcxs      =   cloc2x + (cmu * cdir2x);
04264     pcys      =   cloc2y + (cmu * cdir2y);
04265   }
04266 
04267 #ifdef DEBUG_OUTLINE_SURROUNDING
04268   fprintf( stderr, "    bdet=%f, cdet=%f, bmu=%f, bnu=%f, cmu=%f, cnu=%f\n",
04269           bdet, cdet, bmu, bnu, cmu, cnu);
04270 #endif
04271 
04272   /* Analyse coefficients and decide on numerical stability. If suggesting,
04273      overwrite, using another relation. Here, we assume that at least the
04274      solution at *one* end of the curve is stable. */
04275   if ( bmu < 0.1 ) {
04276     pbxs = ((8 * (nx + p6x) - paxs - pdxs) / 3.0) - pcxs;
04277     pbys = ((8 * (ny + p6y) - pays - pdys) / 3.0) - pcys;
04278   }
04279   if ( cmu < 0.1 ) {
04280     pcxs = ((8 * (nx + p6x) - paxs - pdxs) / 3.0) - pbxs;
04281     pcys = ((8 * (ny + p6y) - pays - pdys) / 3.0) - pbys;
04282   }
04283   
04284   
04285   /* Store the resulting displacement values in the ppoints-struct so
04286      they can be used for path construction. We use the "intersect" member
04287      because in this case nothing is related to "previous" or "next".*/
04288 #ifdef DEBUG_OUTLINE_SURROUNDING
04289   fprintf( stderr, "    pbx=%f, pbxs=%f, bxshift=%f, pby=%f, pbys=%f, byshift=%f\n",
04290           pbx, pbxs, pbxs-pbx, pby, pbys, pbys-pby);
04291   fprintf( stderr, "    pcx=%f, pcxs=%f, cxshift=%f, pcy=%f, pcys=%f, cyshift=%f\n",
04292           pcx, pcxs, pcxs-pcx, pcy, pcys, pcys-pcy);
04293 #endif
04294   ppoints[currind].dxir    = pbxs - pbx;
04295   ppoints[currind].dyir    = pbys - pby;
04296   ppoints[currind+1].dxir  = pcxs - pcx;
04297   ppoints[currind+1].dyir  = pcys - pcy;
04298 
04299   return;
04300   
04301 }
04302 
04303 
04304 static void intersectRight( long index, double halfwidth, long flag)
04305 {
04306   double r2  = 0.0;
04307   double det = 0.0;
04308   double dxprev;
04309   double dyprev;
04310   double dxnext;
04311   double dynext;
04312   
04313   
04314   /* In order to determine the intersection between the two
04315      prolongations at the path point under consideration, we use
04316      the Hesse Normal Form, multiplied with r.
04317 
04318      dx * x + dy + y + r^2 = 0
04319 
04320      Here, r is the distance from the origin, that is, from the path point
04321      under consideration. */
04322 
04323   /* Check for start and ending of non-closed paths */
04324   if ( flag == INTERSECT_PREVIOUS ) {
04325     ppoints[index].dxir = ppoints[index].dxpr;
04326     ppoints[index].dyir = ppoints[index].dypr;
04327     /* Correct shape to be "straight" at ending point! */
04328     ppoints[index].shape = CURVE_STRAIGHT;
04329     return;
04330   }
04331   if ( flag == INTERSECT_NEXT ) {
04332     ppoints[index].dxir = ppoints[index].dxnr;
04333     ppoints[index].dyir = ppoints[index].dynr;
04334     /* Correct shape to be "straight" at starting point! */
04335     ppoints[index].shape = CURVE_STRAIGHT;
04336     return;
04337   }
04338 
04339   /* OK, we actually compute an intersection */
04340   dxprev = ppoints[index].dxpr;
04341   dyprev = ppoints[index].dypr;
04342   dxnext = ppoints[index].dxnr;
04343   dynext = ppoints[index].dynr;
04344 
04345   /* Compute distance square */
04346   r2 = halfwidth * halfwidth;
04347 
04348   /* Check the determinant. If it is zero, the two lines are parallel
04349      and also must touch at atleast one location,
04350      so that there are an infinite number of solutions. In this case,
04351      we compute the average position and are done. */
04352   if ( (det = ((dyprev * dxnext) - (dynext * dxprev))) == 0.0 ) {
04353     ppoints[index].dxir = 0.5 * (dxprev + dxnext);
04354     ppoints[index].dyir = 0.5 * (dyprev + dynext);
04355 #ifdef DEBUG_OUTLINE_SURROUNDING
04356     fprintf( stderr, "intersectRight(0): dxprev=%f, dxnext=%f,  dxres=%f, dyprev=%f, dynext=%f, dyres=%f\n",
04357             dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir);
04358 #endif
04359     return;
04360   }
04361   
04362   /* OK, there seems to be a unique solution, compute it */
04363   if ( dxprev != 0.0 ) {
04364     ppoints[index].dyir =  r2 * (dxnext - dxprev) / det;
04365     ppoints[index].dxir = (r2 - (dyprev * ppoints[index].dyir)) / dxprev; /* - ? */
04366 #ifdef DEBUG_OUTLINE_SURROUNDING
04367     fprintf( stderr, "intersectRight(1): dxprev=%f, dxnext=%f,  dxres=%f, dyprev=%f, dynext=%f, dyres=%f\n",
04368             dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir);
04369 #endif
04370   }
04371   else {
04372     ppoints[index].dyir = -r2 * (dxprev - dxnext) / det;
04373     ppoints[index].dxir = (r2 - (dynext * ppoints[index].dyir)) / dxnext; /* - ? */
04374 #ifdef DEBUG_OUTLINE_SURROUNDING
04375     fprintf( stderr, "intersectRight(2): dxprev=%f, dxnext=%f,  dxres=%f, dyprev=%f, dynext=%f, dyres=%f\n",
04376             dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir);
04377 #endif
04378   }
04379   
04380   return;
04381      
04382 }
04383 
04384 
04385 
04386 /* linkNode(): Insert prolongation lines at nodes. */
04387 static void linkNode( long index, int position, int orientation)
04388 {
04389   struct segment* B;
04390   double dx = 0.0;
04391   double dy = 0.0;
04392 
04393   if ( orientation == PATH_RIGHT ) {
04394     /* We are constructing the right hand side path */
04395     if ( position == PATH_START ) {
04396       /* We are starting a new segment. Link from current point to ideally
04397         next-shifted point of segment. */
04398       if ( ppoints[index].shape == CURVE_CONCAVE ) {
04399        /* prolongate from original curve point to ideally next-shifted point */
04400        dx = ppoints[index].dxnr;
04401        dy = ppoints[index].dynr;
04402 #ifdef DEBUG_OUTLINE_SURROUNDING
04403        fprintf( stderr, "    Right Path Concave at PP %ld. Prolongation from onCurve to ideal: (%f,%f)\n",
04404                index, dx, dy);
04405 #endif
04406       }
04407       else if ( ppoints[index].shape == CURVE_CONVEX ) {
04408        /* prolongate from intersecion point to ideally next-shifted point */
04409        dx = ppoints[index].dxnr - ppoints[index].dxir;
04410        dy = ppoints[index].dynr - ppoints[index].dyir;
04411 #ifdef DEBUG_OUTLINE_SURROUNDING
04412        fprintf( stderr, "RP:  Convex at PP %ld. Prolongation from intersection to ideal: (%f,%f)\n",
04413                index, dx, dy);
04414 #endif
04415       }
04416     }
04417     else if ( position == PATH_END ) {
04418       /* We are ending the current segment. Link from ideally prev-shifted point
04419         to the appropriate ending point. */
04420       if ( ppoints[index].shape == CURVE_CONCAVE ) {
04421        /* prolongate from ideally prev-shifted point to original curve point. */
04422        dx = - ppoints[index].dxpr;
04423        dy = - ppoints[index].dypr;
04424 #ifdef DEBUG_OUTLINE_SURROUNDING
04425        fprintf( stderr, "RP:  Concave at PP %ld. Prolongation from ideal to onCurve: (%f,%f)\n",
04426                index, dx, dy);
04427 #endif
04428       }
04429       else if ( ppoints[index].shape == CURVE_CONVEX ) {
04430        /* prolongate from ideally prev-shifted point to intersection point. */
04431        dx = ppoints[index].dxir - ppoints[index].dxpr;
04432        dy = ppoints[index].dyir - ppoints[index].dypr;
04433 #ifdef DEBUG_OUTLINE_SURROUNDING
04434        fprintf( stderr, "RP:  Convex at PP %ld. Prolongation from ideal to intersection: (%f,%f)\n",
04435                index, dx, dy);
04436 #endif
04437       }
04438     } /* if ( PATH_END ) */
04439   } /* if ( PATH_RIGHT ) */
04440   else if ( orientation == PATH_LEFT ) {
04441 
04442     /* We are constructing the left hand side path. Some notions have to be
04443        reverted (e.g. concavity vs. convexity and next vs. previous)! */
04444     if ( position == PATH_START ) {
04445       /* We are starting a new segment. Link from current point to ideally
04446         next-shifted point of segment. */
04447       if ( ppoints[index].shape == CURVE_CONVEX ) {
04448        /* prolongate from original curve point to ideally next-shifted point.
04449           Remember: next --> prev! */
04450        dx = - (ppoints[index].dxpr);
04451        dy = - (ppoints[index].dypr);
04452 #ifdef DEBUG_OUTLINE_SURROUNDING
04453        fprintf( stderr, "LP:  Concave at PP %ld. Prolongation from onCurve to ideal: (%f,%f)\n",
04454                index, dx, dy);
04455 #endif
04456       }
04457       else if ( ppoints[index].shape == CURVE_CONCAVE ) {
04458        /* prolongate from intersecion point to ideally next-shifted point */
04459        dx = - (ppoints[index].dxpr - ppoints[index].dxir);
04460        dy = - (ppoints[index].dypr - ppoints[index].dyir);
04461 #ifdef DEBUG_OUTLINE_SURROUNDING
04462        fprintf( stderr, "LP:  Convex at PP %ld. Prolongation from intersection to ideal: (%f,%f)\n",
04463                index, dx, dy);
04464 #endif
04465       }
04466     }/* if ( PATH_START ) */
04467     else if ( position == PATH_END ) {
04468       /* We are ending the current segment. Link from ideally prev-shifted point
04469         to the appropriate ending point. */
04470       if ( ppoints[index].shape == CURVE_CONVEX ) {
04471        /* prolongate from ideally prev-shifted point to original curve point. */
04472        dx = ppoints[index].dxnr;
04473        dy = ppoints[index].dynr;
04474 #ifdef DEBUG_OUTLINE_SURROUNDING
04475        fprintf( stderr, "LP:  Concave at PP %ld. Prolongation from ideal to onCurve: (%f,%f)\n",
04476                index, dx, dy);
04477 #endif
04478       }
04479       else if ( ppoints[index].shape == CURVE_CONCAVE ) {
04480        /* prolongate from ideally prev-shifted point to intersection point. */
04481        dx = - (ppoints[index].dxir - ppoints[index].dxnr);
04482        dy = - (ppoints[index].dyir - ppoints[index].dynr);
04483 #ifdef DEBUG_OUTLINE_SURROUNDING
04484        fprintf( stderr, "LP:  Convex at PP %ld. Prolongation from ideal to intersection: (%f,%f)\n",
04485                index, dx, dy);
04486 #endif
04487       }
04488     } /* if ( PATH_END ) */
04489   } /* if ( PATH_LEFT ) */
04490 
04491   if ( (dx != 0.0) || (dy != 0.0) ) {
04492 #ifdef DUMPDEBUGPATH
04493     if ( psfile != NULL )
04494       fprintf( psfile, "%f %f t1sprolongate\n", dx*up, dy*up);
04495 #endif
04496     B = Loc( CharSpace, dx, dy);
04497     path = Join(path, Line(B));
04498   }
04499   
04500   return;
04501   
04502 }