1 /* $XConsortium: type1.c,v 1.5 91/10/10 11:20:06 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
3  * All Rights Reserved
4  * Copyright Lexmark International, Inc. 1991
5  * All Rights Reserved
6  * Portions Copyright (c) 1990 Adobe Systems Incorporated.
7  * All Rights Reserved
8  *
9  * License to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation, and that the name of IBM or Lexmark or Adobe
14  * not be used in advertising or publicity pertaining to distribution of
15  * the software without specific, written prior permission.
16  *
17  * IBM, LEXMARK, AND ADOBE PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY
18  * WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT
19  * LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20  * PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE
21  * ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING
22  * ANY DUTY TO SUPPORT OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY
23  * PORTION OF THE SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM,
24  * LEXMARK, OR ADOBE) ASSUMES THE ENTIRE COST OF ALL SERVICING, REPAIR AND
25  * CORRECTION.  IN NO EVENT SHALL IBM, LEXMARK, OR ADOBE BE LIABLE FOR ANY
26  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
27  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
28  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
29  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30  */
31 
32 /*********************************************************************/
33 /*                                                                   */
34 /* Type 1 module - Converting fonts in Adobe Type 1 Font Format      */
35 /*                 to scaled and hinted paths for rasterization.     */
36 /*                 Files: type1.c, type1.h, and blues.h.             */
37 /*                                                                   */
38 /* Authors:   Sten F. Andler, IBM Almaden Research Center            */
39 /*                 (Type 1 interpreter, stem & flex hints)           */
40 /*                                                                   */
41 /*            Patrick A. Casey, Lexmark International, Inc.          */
42 /*                 (Font level hints & stem hints)                   */
43 /*                                                                   */
44 /*********************************************************************/
45 
46 
47 /* Write debug info into a PostScript file? */
48 /* #define DUMPDEBUGPATH */
49 /* If Dumping a debug path, should we dump both character and
50    outline path? Warning: Do never enable this, unless, your name
51    is Rainer Menzner and you know what you are doing! */
52 /* #define DUMPDEBUGPATHBOTH */
53 
54 /* Generate a bunch of stderr output to understand and debug
55    the generation of outline surrounding curves */
56 /* #define DEBUG_OUTLINE_SURROUNDING */
57 
58 #define SUBPATH_CLOSED     1
59 #define SUBPATH_OPEN       0
60 
61 /******************/
62 /* Include Files: */
63 /******************/
64 #include  "types.h"
65 #include  <stdio.h>          /* a system-dependent include, usually */
66 #include  <math.h>
67 #include  <stdlib.h>
68 
69 #include  "objects.h"
70 #include  "spaces.h"
71 #include  "paths.h"
72 #include  "fonts.h"        /* understands about TEXTTYPEs */
73 #include  "pictures.h"     /* understands about handles */
74 
75 typedef struct xobject xobject;
76 #include  "util.h"       /* PostScript objects */
77 #include  "fontfcn.h"
78 #include  "blues.h"          /* Blues structure for font-level hints */
79 
80 
81 /* Considerations about hinting (2002-07-11, RMz (Author of t1lib))
82 
83    It turns out that the hinting code as used until now produces some
84    artifacts in which may show up in suboptimal bitmaps. I have therefore
85    redesigned the algorithm. It is generally a bad idea to hint every
86    point that falls into a stem hint.
87 
88    The idea is to hint only points for
89    which at least one of the two neighboring curve/line segments is aligned
90    with the stem in question. For curves, we are speaking about the
91    tangent line, that is, the line defined by (p1-p2) or (p3-p4).
92 
93    For vertical stems this means, that only points which are connected
94    exactly into vertical direction are hinted. That is, the dx of the
95    respective curve vanishes. For horizontal stems, accordingly, dy must
96    vanish at least on one hand side of the point in order to be considered
97    as a stem.
98 
99    Unfortunately this principle requires information about both sides of the
100    neighborhood of the point in question. In other words, it is not possible
101    to define a segment completely until the next segment has been inspected.
102    The idea thus is not compatible with the code in this file.
103 
104    Furthermore, if certain points of a character outline are hinted according
105    to the stem hint info from the charstring, the non-hinted points may not be
106    left untouched. This would lead to very strong artifacts at small sizes,
107    especially if characters are defined in terms of curves. This is predominantly
108    the case for ComputerModern, for example.
109 
110    To conclude, it is best to build a point list from the character description
111    adjust the non-hinted points after hinting has been completely finished.
112 
113 
114    Another rule we should state is
115 
116    We can work around this by not directly connecting the path segments at
117    the end of the lineto/curveto's, but rather deferring this to the beginning
118    of the next path constructing function. It's not great but it should work.
119 
120    The functions that produce segments are
121 
122    1) RMoveTo()
123    2) RLineto()
124    3) RRCurveTo()
125    4) DoClosePath()
126 
127    Their code is moved into the switch statement of the new function
128    handleCurrentSegment(). This function is called when a new segment generating
129    operation has been decoded from the charstring. At this point a serious
130    decision about how to hint the points is possible.
131 
132    ...
133 */
134 
135 
136 /* The following struct is used to record points that define a path
137    in absolute charspace coordinates. x and y describe the location and
138    hinted, if greater 0, indicates that this point has been hinted. Bit 0
139    (0x1) indicates vertically adjusted and Bit 1 (0x2) indicates
140    horizontally adjusted. If hinted == -1, this point is not to be hinted
141    at all. This, for example, is the case for a a (H)SBW command.
142 
143    The member type can be one of
144 
145    PPOINT_SBW                --> initial path point as setup by (H)SBW
146    PPOINT_MOVE               --> point that finishes a MOVE segment
147    PPOINT_LINE               --> point that finishes a LINE segment
148    PPOINT_BEZIER_B           --> second point of a BEZIER segment
149    PPOINT_BEZIER_C           --> third point of a BEZIER segment
150    PPOINT_BEZIER_D           --> fourth point of a BEZIER segment
151    PPOINT_CLOSEPATH          --> a ClosePath command
152    PPOINT_ENDCHAR            --> an EndChar command
153    PPOINT_SEAC               --> a Standard Encoding Accented Char command
154    PPOINT_NONE               --> an invalid entry
155 
156 
157    Note: BEZIER_B and BEZIER_C points generally cannot be flagged as
158    being hinted because are off-curve points.
159 */
160 typedef struct
161 {
162   double x;              /* x-coordinate */
163   double y;              /* y-coordinate */
164   double ax;             /* adjusted x-coordinate */
165   double ay;             /* adjusted y-coordinate */
166   double dxpr;           /* x-shift in right path due to incoming segment (previous) */
167   double dypr;           /* y-shift in right path due to incoming segment (previous) */
168   double dxnr;           /* x-shift in right path due to outgoing segment (next) */
169   double dynr;           /* y-shift in right path due to incoming segment (next) */
170   double dxir;           /* x-shift in right path resulting from prologation of the linkend tangents (intersect) */
171   double dyir;           /* y-shift in right path resulting from prologation of the linkend tangents (intersect) */
172   double dist2prev;      /* distance to the previous point in path (used only for stroking) */
173   double dist2next;      /* distance to the next point in path (used only for stroking) */
174   enum
175   {
176     PPOINT_SBW,
177     PPOINT_MOVE,
178     PPOINT_LINE,
179     PPOINT_BEZIER_B,
180     PPOINT_BEZIER_C,
181     PPOINT_BEZIER_D,
182     PPOINT_CLOSEPATH,
183     PPOINT_ENDCHAR,
184     PPOINT_SEAC,
185     PPOINT_NONE
186   } type;                /* type of path point */
187   signed char   hinted;  /* is this point hinted? */
188   unsigned char shape;   /* is the outline concave or convex or straight at this point? This flag
189 			    is only relevant for onCurve points in the context of stroking! */
190 } PPOINT;
191 
192 #define CURVE_NONE            0x00
193 #define CURVE_STRAIGHT        0x01
194 #define CURVE_CONVEX          0x02
195 #define CURVE_CONCAVE         0x03
196 
197 #ifdef DEBUG_OUTLINE_SURROUNDING
198 static char* pptypes[] = {
199   "PPOINT_SBW",
200   "PPOINT_MOVE",
201   "PPOINT_LINE",
202   "PPOINT_BEZIER_B",
203   "PPOINT_BEZIER_C",
204   "PPOINT_BEZIER_D",
205   "PPOINT_CLOSEPATH",
206   "PPOINT_ENDCHAR",
207   "PPOINT_SEAC"
208 };
209 static char* ppshapes[] = {
210   "SHAPE_OFFCURVE",
211   "SHAPE_STRAIGHT",
212   "SHAPE_CONVEX",
213   "SHAPE_CONCAVE"
214 };
215 #endif
216 
217 
218 /* The PPOINT structs are organized in an array which is allocated
219    in chunks of 256 entries. A new point is allocated by a call to
220    nextPPoint and returns the index in the array of the newly
221    allocated point. */
222 static PPOINT* ppoints       = NULL;
223 static long numppoints       = 0;
224 static long numppointchunks  = 0;
225 static int  closepathatfirst = 0;
226 
nextPPoint(void)227 static long nextPPoint( void)
228 {
229   ++numppoints;
230   /* Check whether to reallocate */
231   if ( numppoints > (numppointchunks * 256) ) {
232     ++numppointchunks;
233     ppoints = (PPOINT*) realloc( ppoints, (numppointchunks * 256) * sizeof( PPOINT));
234   }
235   /* return the current index */
236   return numppoints-1;
237 }
238 
239 static void createFillPath( void);
240 static void createStrokePath( double strokewidth, int subpathclosed);
241 static void createClosedStrokeSubPath( long startind, long stopind,
242 				       double strokewidth, int subpathclosed);
243 static long computeDistances( long startind, long stopind, int subpathclosed);
244 static void transformOnCurvePathPoint( double strokewidth,
245 				       long prevind, long currind, long lastind);
246 static void transformOffCurvePathPoint( double strokewidth, long currind);
247 /* values for flag:
248    INTERSECT_PREVIOUS:     only take previous path segment into account.
249    INTERSECT_NEXT:         only take next path segment into account.
250    INTERSECT_BOTH:         do a real intersection
251 */
252 #define INTERSECT_PREVIOUS    -1
253 #define INTERSECT_NEXT         1
254 #define INTERSECT_BOTH         0
255 static void intersectRight( long index, double halfwidth, long flag);
256 /* values for orientation:
257    PATH_LEFT:              we are constructing the left path.
258    PATH_RIGHT:             we are constructing the right path.
259 */
260 #define PATH_LEFT              1
261 #define PATH_RIGHT             0
262 /* values for position:
263    PATH_START:             current point starts the current path (use next-values).
264    PATH_END:               current point ends the current path (use prev-values).
265 */
266 #define PATH_START             0
267 #define PATH_END               1
268 static void linkNode( long index, int position, int orientation);
269 
270 
271 static long handleNonSubPathSegments( long pindex);
272 static void handleCurrentSegment( long pindex);
273 static void adjustBezier( long pindex);
274 
275 static double  size;
276 static double scxx, scyx, scxy, scyy;
277 static double  up;
278 
279 #ifdef DUMPDEBUGPATH
280 static FILE*   psfile = NULL;
281 static void PSDumpProlog( FILE* fp);
282 static void PSDumpEpilog( FILE* fp);
283 #endif
284 
285 /* variables for querying SEAC from external */
286 static int isseac                = 0;
287 static unsigned char seacbase    = 0;
288 static unsigned char seacaccent  = 0;
289 
290 
291 /**********************************/
292 /* Type1 Constants and Structures */
293 /**********************************/
294 #define MAXSTACK 24        /* Adobe Type1 limit */
295 #define MAXCALLSTACK 10    /* Adobe Type1 limit */
296 #define MAXPSFAKESTACK 32  /* Max depth of fake PostScript stack (local) */
297 #define MAXSTRLEN 512      /* Max length of a Type 1 string (local) */
298 #define MAXLABEL 256       /* Maximum number of new hints */
299 #define MAXSTEMS 512       /* Maximum number of VSTEM and HSTEM hints */
300 #define EPS 0.001          /* Small number for comparisons */
301 
302 /************************************/
303 /* Adobe Type 1 CharString commands */
304 /************************************/
305 #define HSTEM        1
306 #define VSTEM        3
307 #define VMOVETO      4
308 #define RLINETO      5
309 #define HLINETO      6
310 #define VLINETO      7
311 #define RRCURVETO    8
312 #define CLOSEPATH    9
313 #define CALLSUBR    10
314 #define RETURN      11
315 #define ESCAPE      12
316 #define HSBW        13
317 #define ENDCHAR     14
318 #define RMOVETO     21
319 #define HMOVETO     22
320 #define VHCURVETO   30
321 #define HVCURVETO   31
322 
323 /* The following charstring code appears in some old Adobe font files
324    in space and .notdef character and does not seems to do anything
325    useful aside from taking two args from the stack. We allow this
326    command and ignore it. The source code of ghostscript states that
327    this command is obsolete *and* undocumented.
328    This code may also appear in an Escape-sequence! */
329 #define UNKNOWN_15  15
330 
331 /*******************************************/
332 /* Adobe Type 1 CharString Escape commands */
333 /*******************************************/
334 #define DOTSECTION       0
335 #define VSTEM3           1
336 #define HSTEM3           2
337 #define SEAC             6
338 #define SBW              7
339 #define DIV             12
340 #define CALLOTHERSUBR   16
341 #define POP             17
342 #define SETCURRENTPOINT 33
343 
344 
345 /* Note: We use routines from libm because in the original macro definitions,
346    the evaluation order of tmpx is undefined by C-standard! */
347 #define USE_MATHLIB_ROUTINES
348 
349 #ifdef USE_MATHLIB_ROUTINES
350 
351 #define FABS(x) (fabs (x))
352 #define CEIL(x) ((LONG) ceil (x))
353 #define FLOOR(x) ((LONG) floor (x))
354 
355 #else
356 
357 /*****************/
358 /* Useful macros */
359 /*****************/
360 static DOUBLE tmpx;  /* Store macro argument in tmpx to avoid re-evaluation */
361 static LONG tmpi;    /* Store converted value in tmpi to avoid re-evaluation */
362 #define FABS(x) (((tmpx = (x)) < 0.0) ? -tmpx : tmpx)
363 #define CEIL(x) (((tmpi = (LONG) (tmpx = (x))) < tmpx) ? ++tmpi : tmpi)
364 #define FLOOR(x) (((tmpi = (LONG) (tmpx = (x))) > tmpx) ? --tmpi : tmpi)
365 
366 #endif
367 
368 #define ROUND(x) FLOOR((x) + 0.5)
369 #define ODD(x) (((int)(x)) & 01)
370 
371 #define CC IfTrace1(TRUE, "Char \"%s\": ", currentchar)
372 
373 /* To make some compiler happy we have to care about return types! */
374 #define Errori {errflag = TRUE; return 0;}    /* integer */
375 #define Errord {errflag = TRUE; return 0.0;}  /* double */
376 #define Errorv {errflag = TRUE; return;}      /* void */
377 
378 #define Error0i(errmsg) { CC; IfTrace0(TRUE, errmsg); Errori;}
379 #define Error0d(errmsg) { CC; IfTrace0(TRUE, errmsg); Errord;}
380 #define Error0v(errmsg) { CC; IfTrace0(TRUE, errmsg); Errorv;}
381 
382 #define Error1i(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errori;}
383 #define Error1d(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errord;}
384 #define Error1v(errmsg,arg) { CC; IfTrace1(TRUE, errmsg, arg); Errorv;}
385 
386 /********************/
387 /* global variables */
388 /********************/
389 struct stem {              /* representation of a STEM hint */
390   int vertical;                 /* TRUE if vertical, FALSE otherwise */
391   DOUBLE x, dx;                 /* interval of vertical stem */
392   DOUBLE y, dy;                 /* interval of horizontal stem */
393   DOUBLE alx, aldx;             /* interval of grid-aligned vertical stem */
394   DOUBLE aly, aldy;             /* interval of grid-aligned horizontal stem */
395   double lbhintval;             /* adjustment value for left or bottom hint */
396   double rthintval;             /* adjustment value for right ir top hint */
397 };
398 
399 /******************************************************/
400 /* Subroutines and statics for the Type1Char routines */
401 /******************************************************/
402 
403 static int strindex; /* index into PostScript string being interpreted */
404 static double currx, curry;           /* accumulated x and y values */
405 static double hcurrx, hcurry;         /* accumulated values with hinting */
406 
407 
408 struct callstackentry {
409   psobj *currstrP;        /* current CharStringP */
410   int currindex;          /* current strindex */
411   unsigned short currkey; /* current decryption key */
412   };
413 
414 static DOUBLE Stack[MAXSTACK];
415 static int Top;
416 static struct callstackentry CallStack[MAXCALLSTACK];
417 static int CallTop;
418 static DOUBLE PSFakeStack[MAXPSFAKESTACK];
419 static int PSFakeTop;
420 
421 
422 extern struct XYspace *IDENTITY;
423 
424 static DOUBLE escapementX, escapementY;
425 static DOUBLE sidebearingX, sidebearingY;
426 static DOUBLE accentoffsetX, accentoffsetY;
427 
428 static struct segment *path;    /* path of basechar */
429 static struct segment *apath;   /* pass of accent char */
430 static int errflag;
431 
432 /*************************************************/
433 /* Global variables to hold Type1Char parameters */
434 /*************************************************/
435 static char *Environment;
436 static char *currentchar;
437 static struct XYspace *CharSpace;
438 static psobj *CharStringP, *SubrsP, *OtherSubrsP;
439 static int *ModeP;
440 
441 /************************/
442 /* Forward declarations */
443 /************************/
444 static DOUBLE Div();
445 static DOUBLE PSFakePop();
446 static int DoCommand();
447 static int Escape();
448 static int HStem();
449 static int VStem();
450 static int RLineTo();
451 static int RRCurveTo();
452 static int DoClosePath();
453 static int CallSubr();
454 static int Return();
455 static int EndChar();
456 static int RMoveTo();
457 static int DotSection();
458 static int Seac();
459 static int Sbw();
460 static int CallOtherSubr();
461 static int SetCurrentPoint();
462 
463 /******************************************************/
464 /* statics for Font level hints (Blues) (see blues.h) */
465 /******************************************************/
466 static struct blues_struct *blues; /* the blues structure */
467 static struct alignmentzone alignmentzones[MAXALIGNMENTZONES];
468 int numalignmentzones;          /* total number of alignment zones */
469 
470 /****************************************************************/
471 /* Subroutines for the Font level hints (Alignment zones, etc.) */
472 /****************************************************************/
473 
474 
475 
476 /* Flags to control the rasterizer */
477 #define T1_IGNORE_FORCEBOLD           0x0001
478 #define T1_IGNORE_FAMILYALIGNMENT     0x0002
479 #define T1_IGNORE_HINTING             0x0004
480 
481 #define T1_DEBUG_LINE                 0x0100
482 #define T1_DEBUG_REGION               0x0200
483 #define T1_DEBUG_PATH                 0x0400
484 #define T1_DEBUG_FONT                 0x0800
485 #define T1_DEBUG_HINT                 0x1000
486 
487 int T1_Type1OperatorFlags; /* for manipulation from t1lib */
488 
489 
SetRasterFlags(void)490 static void SetRasterFlags( void)
491 {
492 
493   if (T1_Type1OperatorFlags & T1_IGNORE_HINTING)
494     ProcessHints=0;
495   else
496     ProcessHints=1;
497 
498   if ( T1_Type1OperatorFlags & T1_DEBUG_LINE)
499     LineDebug=1;
500   else
501     LineDebug=0;
502   if ( T1_Type1OperatorFlags & T1_DEBUG_REGION)
503     RegionDebug=1;
504   else
505     RegionDebug=0;
506   if ( T1_Type1OperatorFlags & T1_DEBUG_PATH)
507     PathDebug=1;
508   else
509     PathDebug=0;
510   if ( T1_Type1OperatorFlags & T1_DEBUG_FONT)
511     FontDebug=1;
512   else
513     FontDebug=0;
514   if ( T1_Type1OperatorFlags & T1_DEBUG_HINT)
515     HintDebug=1;
516   else
517     HintDebug=0;
518   return;
519 
520 }
521 
522 
523 /******************************************/
524 /* Fill in the alignment zone structures. */
525 /******************************************/
ComputeAlignmentZones()526 static int ComputeAlignmentZones()
527 {
528   int i;
529   DOUBLE dummy, bluezonepixels, familyzonepixels;
530   struct segment *p;
531 
532   numalignmentzones = 0;     /* initialize total # of zones */
533 
534   /* Remarks by RMz (Author of t1lib): The handling of substitution of
535      the BlueValues by the FamilyBlues and correspondingly for the
536      OtherBlues and FamilyOtherBlues is not clearly documented.
537      These are the problems:
538 
539      1) Does the number of FamilyBlues entries need to be identical to
540         that of BlueValues?
541 
542      2) Obviously, the order of the alignment zones in the BlueValues
543         and the FamilyBlues need not be same (see TimesBold.pfa)
544 
545      3) Is it wise/recommended to make the substitution on a per
546         alignment-zone level or global, i.e., if once then for all
547 	zones?
548 
549      4) The principle found below, checking the delta-height of an
550         alignment-zone and making a decision based on this is incorrect.
551 	The decision has to be done according to absolute pixel values
552 	at which a feature would be rendered with the BlueValues and the
553 	FamilyBlues respectively.
554 
555      To conclude, it seems better to disable the Family-feature until
556      these things are well-known/defined.
557      */
558 
559   /* do the BlueValues zones */
560   for (i = 0; i < blues->numBlueValues; i +=2, ++numalignmentzones) {
561     /* the 0th & 1st numbers in BlueValues are for a bottom zone */
562     /* the rest are topzones */
563     if (i == 0)           /* bottom zone */
564       alignmentzones[numalignmentzones].topzone = FALSE;
565     else                  /* top zone */
566       alignmentzones[numalignmentzones].topzone = TRUE;
567     /* Check FamilyAlignment suppression */
568     if ( (T1_Type1OperatorFlags & T1_IGNORE_FAMILYALIGNMENT)==0) {
569       if (i < blues->numFamilyBlues) {    /* we must consider FamilyBlues */
570 	p = ILoc(CharSpace,0,blues->BlueValues[i] - blues->BlueValues[i+1]);
571 	QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
572 	Destroy(p);
573 	p = ILoc(CharSpace,0,blues->FamilyBlues[i] - blues->FamilyBlues[i+1]);
574 	QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
575 	Destroy(p);
576 	/* is the difference in size of the zones less than 1 pixel? */
577 	if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
578 	  /* use the Family zones */
579 	  alignmentzones[numalignmentzones].bottomy =
580 	    blues->FamilyBlues[i];
581 	  alignmentzones[numalignmentzones].topy =
582 	    blues->FamilyBlues[i+1];
583 #ifdef DUMPDEBUGPATH
584 	  if ( psfile != NULL ) {
585 	    if ( alignmentzones[numalignmentzones].topzone == TRUE )
586 	      fprintf( psfile, "%f %f t1topzone\n", (blues->FamilyBlues[i])*up,
587 		       (blues->BlueValues[i+1])*up);
588 	    else
589 	      fprintf( psfile, "%f %f t1bottomzone\n", (blues->FamilyBlues[i])*up,
590 		       (blues->BlueValues[i+1])*up);
591 	  }
592 #endif
593 	  continue;
594 	}
595       }
596     }
597     /* use this font's Blue zones */
598     alignmentzones[numalignmentzones].bottomy = blues->BlueValues[i];
599     alignmentzones[numalignmentzones].topy = blues->BlueValues[i+1];
600 #ifdef DUMPDEBUGPATH
601     if ( psfile != NULL ) {
602       if ( alignmentzones[numalignmentzones].topzone == TRUE )
603 	fprintf( psfile, "%f %f t1topzone\n", (blues->BlueValues[i])*up,
604 		 (blues->BlueValues[i+1])*up);
605       else
606 	fprintf( psfile, "%f %f t1bottomzone\n", (blues->BlueValues[i])*up,
607 		 (blues->BlueValues[i+1])*up);
608     }
609 #endif
610   }
611 
612   /* do the OtherBlues zones */
613   for (i = 0; i < blues->numOtherBlues; i +=2, ++numalignmentzones) {
614     /* all of the OtherBlues zones are bottom zones */
615     alignmentzones[numalignmentzones].topzone = FALSE;
616     /* Check FamilyAlignment suppression */
617     if ( (T1_Type1OperatorFlags & T1_IGNORE_FAMILYALIGNMENT)==0) {
618       if (i < blues->numFamilyOtherBlues) {/* consider FamilyOtherBlues  */
619 	p = ILoc(CharSpace,0,blues->OtherBlues[i] - blues->OtherBlues[i+1]);
620 	QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
621 	Destroy(p);
622 	p = ILoc(CharSpace,0,blues->FamilyOtherBlues[i] -
623 		 blues->FamilyOtherBlues[i+1]);
624 	QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
625 	Destroy(p);
626 	/* is the difference in size of the zones less than 1 pixel? */
627 	if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
628 	  /* use the Family zones */
629 	  alignmentzones[numalignmentzones].bottomy =
630 	    blues->FamilyOtherBlues[i];
631 	  alignmentzones[numalignmentzones].topy =
632 	    blues->FamilyOtherBlues[i+1];
633 #ifdef DUMPDEBUGPATH
634 	  if ( psfile != NULL ) {
635 	    fprintf( psfile, "%f %f t1bottomzone\n", (blues->FamilyOtherBlues[i])*up,
636 		     (blues->FamilyOtherBlues[i+1])*up);
637 	  }
638 #endif
639 	  continue;
640 	}
641       }
642     }
643     /* use this font's Blue zones (as opposed to the Family Blues */
644     alignmentzones[numalignmentzones].bottomy = blues->OtherBlues[i];
645     alignmentzones[numalignmentzones].topy = blues->OtherBlues[i+1];
646 #ifdef DUMPDEBUGPATH
647     if ( psfile != NULL ) {
648       fprintf( psfile, "%f %f t1bottomzone\n", (blues->OtherBlues[i])*up,
649 	       (blues->OtherBlues[i+1])*up);
650     }
651 #endif
652   }
653   return(0);
654 
655 }
656 
657 /**********************************************************************/
658 /* Subroutines and statics for handling of the VSTEM and HSTEM hints. */
659 /**********************************************************************/
660 int InDotSection;             /* DotSection flag */
661 struct stem stems[MAXSTEMS];  /* All STEM hints */
662 int numstems;                 /* Number of STEM hints */
663 int currstartstem;            /* The current starting stem. */
664 int oldvert, oldhor;          /* Remember hint in effect */
665 int oldhorhalf, oldverthalf;  /* Remember which half of the stem */
666 DOUBLE wsoffsetX, wsoffsetY;  /* White space offset - for VSTEM3,HSTEM3 */
667 int wsset;                    /* Flag for whether we've set wsoffsetX,Y */
668 
InitStems()669 static int InitStems()  /* Initialize the STEM hint data structures */
670 {
671   InDotSection = FALSE;
672   currstartstem = numstems = 0;
673   oldvert = oldhor = -1;
674   return(0);
675 
676 }
677 
678 
679 /*******************************************************************/
680 /* Compute the dislocation that a stemhint should cause for points */
681 /* inside the stem.                                                */
682 /*******************************************************************/
ComputeStem(stemno)683 static int ComputeStem(stemno)
684 int stemno;
685 {
686   int verticalondevice, idealwidth;
687   DOUBLE stemstart, stemwidth;
688   struct segment *p;
689   int i;
690   DOUBLE stembottom, stemtop, flatposition;
691   DOUBLE Xpixels, Ypixels;
692   DOUBLE unitpixels, onepixel;
693   int suppressovershoot, enforceovershoot;
694   DOUBLE stemshift, flatpospixels, overshoot;
695   DOUBLE widthdiff; /* Number of character space units to adjust width */
696   DOUBLE lbhintvalue, rthintvalue;
697   DOUBLE cxx, cyx, cxy, cyy; /* Transformation matrix */
698   int rotated; /* TRUE if character is on the side, FALSE if upright */
699 
700   /************************************************/
701   /* DETERMINE ORIENTATION OF CHARACTER ON DEVICE */
702   /************************************************/
703 
704   QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
705 
706   if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
707     rotated = TRUE; /* Char is on side (90 or 270 degrees), possibly oblique. */
708   else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
709     rotated = FALSE; /* Char is upright (0 or 180 degrees), possibly oblique. */
710   else {
711     stems[stemno].lbhintval = 0.0; /* Char is at non-axial angle, ignore hints. */
712     stems[stemno].rthintval = 0.0;
713     ProcessHints = 0;
714     return(0);
715   }
716 
717   /* Determine orientation of stem */
718 
719   if (stems[stemno].vertical) {
720     verticalondevice = !rotated;
721     stemstart = stems[stemno].x;
722     stemwidth = stems[stemno].dx;
723 #ifdef DUMPDEBUGPATH
724     if ( psfile != NULL )
725       fprintf( psfile, "%f %f t1vstem\n", stemstart*up, stemwidth*up);
726 #endif
727   } else {
728     verticalondevice = rotated;
729     stemstart = stems[stemno].y;
730     stemwidth = stems[stemno].dy;
731 #ifdef DUMPDEBUGPATH
732     if ( psfile != NULL )
733       fprintf( psfile, "%f %f t1hstem\n", stemstart*up, stemwidth*up);
734 #endif
735   }
736 
737   /* Determine how many pixels (non-negative) correspond to 1 character space
738      unit (unitpixels), and how many character space units (non-negative)
739      correspond to one pixel (onepixel). */
740 
741   if (stems[stemno].vertical)
742     p = ILoc(CharSpace, 1, 0);
743   else
744     p = ILoc(CharSpace, 0, 1);
745   QueryLoc(p, IDENTITY, &Xpixels, &Ypixels);
746   Destroy(p);
747   if (verticalondevice)
748     unitpixels = FABS(Xpixels);
749   else
750     unitpixels = FABS(Ypixels);
751 
752   onepixel = 1.0 / unitpixels;
753 
754   /**********************/
755   /* ADJUST STEM WIDTHS */
756   /**********************/
757 
758   widthdiff = 0.0;
759 
760   /* Find standard stem with smallest width difference from this stem */
761   if (stems[stemno].vertical) { /* vertical stem */
762     if (blues->StdVW != 0)      /* there is an entry for StdVW */
763       widthdiff = blues->StdVW - stemwidth;
764     for (i = 0; i < blues->numStemSnapV; ++i) { /* now look at StemSnapV */
765       if (FABS(blues->StemSnapV[i] - stemwidth) < FABS(widthdiff))
766         /* this standard width is the best match so far for this stem */
767         widthdiff = blues->StemSnapV[i] - stemwidth;
768     }
769   } else {                      /* horizontal stem */
770     if (blues->StdHW != 0)      /* there is an entry for StdHW */
771       widthdiff = blues->StdHW - stemwidth;
772     for (i = 0; i < blues->numStemSnapH; ++i) { /* now look at StemSnapH */
773       if (FABS(blues->StemSnapH[i] - stemwidth) < FABS(widthdiff))
774         /* this standard width is the best match so far for this stem */
775         widthdiff = blues->StemSnapH[i] - stemwidth;
776     }
777   }
778 
779   /* Only expand or contract stems if they differ by less than 1 pixel from
780      the closest standard width, otherwise make the width difference = 0. */
781   if (FABS(widthdiff) > onepixel)
782     widthdiff = 0.0;
783 
784   /* Expand or contract stem to the nearest integral number of pixels. */
785   idealwidth = ROUND((stemwidth + widthdiff) * unitpixels);
786   /* Ensure that all stems are at least one pixel wide. */
787   if (idealwidth == 0)
788     idealwidth = 1;
789 
790   /* Apply ForceBold to vertical stems. */
791   if (blues->ForceBold && stems[stemno].vertical &&
792       ((T1_Type1OperatorFlags & T1_IGNORE_FORCEBOLD)==0))
793     /* Force this vertical stem to be at least DEFAULTBOLDSTEMWIDTH wide. */
794     if (idealwidth < DEFAULTBOLDSTEMWIDTH)
795       idealwidth = DEFAULTBOLDSTEMWIDTH;
796   /* Now compute the number of character space units necessary */
797   widthdiff = idealwidth * onepixel - stemwidth;
798 
799   /*********************************************************************/
800   /* ALIGNMENT ZONES AND OVERSHOOT SUPPRESSION - HORIZONTAL STEMS ONLY */
801   /*********************************************************************/
802 
803   stemshift = 0.0;
804 
805   if ( !stems[stemno].vertical ) {
806 
807     /* Get bottom and top boundaries of the stem. */
808     stembottom = stemstart;
809     stemtop = stemstart + stemwidth;
810 
811     /* Find out if this stem intersects an alignment zone (the BlueFuzz  */
812     /* entry in the Private dictionary specifies the number of character */
813     /* units to extend (in both directions) the effect of an alignment   */
814     /* zone on a horizontal stem.  The default value of BlueFuzz is 1.   */
815     for (i = 0; i < numalignmentzones; ++i) {
816       if (alignmentzones[i].topzone) {
817         if (stemtop >= alignmentzones[i].bottomy &&
818             stemtop <= alignmentzones[i].topy + blues->BlueFuzz) {
819           break; /* We found a top-zone */
820         }
821       } else {
822         if (stembottom <= alignmentzones[i].topy &&
823             stembottom >= alignmentzones[i].bottomy - blues->BlueFuzz) {
824           break; /* We found a bottom-zone */
825         }
826       }
827     }
828 
829     if (i < numalignmentzones) { /* We found an intersecting zone (number i). */
830       suppressovershoot = FALSE;
831       enforceovershoot = FALSE;
832 
833       /* When 1 character space unit is rendered smaller than BlueScale
834          device units (pixels), we must SUPPRESS overshoots.  Otherwise,
835          if the top (or bottom) of this stem is more than BlueShift character
836          space units away from the flat position, we must ENFORCE overshoot. */
837 
838       if (unitpixels < blues->BlueScale){
839         suppressovershoot = TRUE;
840       }
841       else{
842         if (alignmentzones[i].topzone){
843           if (stemtop >= alignmentzones[i].bottomy + blues->BlueShift){
844             enforceovershoot = TRUE;
845 	  }
846         else
847           if (stembottom <= alignmentzones[i].topy - blues->BlueShift){
848             enforceovershoot = TRUE;
849 	  }
850 	}
851       }
852 
853 
854       /*************************************************/
855       /* ALIGN THE FLAT POSITION OF THE ALIGNMENT ZONE */
856       /*************************************************/
857 
858       /* Compute the position of the alignment zone's flat position in
859          device space and the amount of shift needed to align it on a
860          pixel boundary. Move all stems this amount. */
861 
862       if (alignmentzones[i].topzone)
863         flatposition = alignmentzones[i].bottomy;
864       else
865         flatposition = alignmentzones[i].topy;
866 
867       /* Find the flat position in pixels */
868       flatpospixels = flatposition * unitpixels;
869 
870       /* Find the stem shift necessary to align the flat
871          position on a pixel boundary, and use this shift for all stems */
872       stemshift = (ROUND(flatpospixels) - flatpospixels) * onepixel;
873 
874       /************************************************/
875       /* HANDLE OVERSHOOT ENFORCEMENT AND SUPPRESSION */
876       /************************************************/
877 
878       /* Compute overshoot amount (non-negative) */
879       if (alignmentzones[i].topzone)
880         overshoot = stemtop - flatposition;
881       else
882         overshoot = flatposition - stembottom;
883 
884       if (overshoot > 0.0) {
885         /* ENFORCE overshoot by shifting the entire stem (if necessary) so that
886            it falls at least one pixel beyond the flat position. */
887 
888         if (enforceovershoot){
889           if (overshoot < onepixel){
890             if (alignmentzones[i].topzone)
891               stemshift += onepixel - overshoot;
892             else
893               stemshift -= onepixel - overshoot;
894 	  }
895 	}
896 
897 
898         /* SUPPRESS overshoot by aligning the stem to the alignment zone's
899            flat position. */
900 
901         if (suppressovershoot){
902           if (alignmentzones[i].topzone)
903             stemshift -= overshoot;
904           else
905             stemshift += overshoot;
906 	}
907       }
908 
909       /************************************************************/
910       /* COMPUTE HINT VALUES FOR EACH SIDE OF THE HORIZONTAL STEM */
911       /************************************************************/
912 
913       /* If the stem was aligned by a topzone, we expand or contract the stem
914          only at the bottom - since the stem top was aligned by the zone.
915          If the stem was aligned by a bottomzone, we expand or contract the stem
916          only at the top - since the stem bottom was aligned by the zone. */
917       if (alignmentzones[i].topzone) {
918         lbhintvalue = stemshift - widthdiff; /* bottom */
919         rthintvalue = stemshift;             /* top    */
920       } else {
921         lbhintvalue = stemshift;             /* bottom */
922         rthintvalue = stemshift + widthdiff; /* top    */
923       }
924 
925       stems[stemno].lbhintval = lbhintvalue;
926       stems[stemno].rthintval = rthintvalue;
927 
928       /* store grid-aligned stems values */
929       stems[stemno].aly       = stemstart + lbhintvalue;
930       stems[stemno].aldy      = stemwidth + widthdiff;
931 
932 #ifdef DUMPDEBUGPATH
933       if ( psfile != NULL )
934 	fprintf( psfile, "%f %f t1alignedhstem\n", (stems[stemno].aly)*up,
935 		 (stems[stemno].aldy)*up);
936 #endif
937       return(0);
938 
939     } /* endif (i < numalignmentzones) */
940 
941     /* We didn't find any alignment zones intersecting this stem, so
942        proceed with normal stem alignment below. */
943 
944   } /* endif (!stems[stemno].vertical) */
945 
946   /* Align stem with pixel boundaries on device */
947   stemstart = stemstart - widthdiff / 2;
948   stemshift = ROUND(stemstart * unitpixels) * onepixel - stemstart;
949 
950   /* Adjust the boundaries of the stem */
951   lbhintvalue = stemshift - widthdiff / 2; /* left  or bottom */
952   rthintvalue = stemshift + widthdiff / 2; /* right or top    */
953 
954   if (stems[stemno].vertical) {
955     stems[stemno].lbhintval = lbhintvalue;
956     stems[stemno].rthintval = rthintvalue;
957 
958     /* store grid-aligned stem values */
959     stems[stemno].alx       = stemstart + stemshift;
960     stems[stemno].aldx      = stemwidth + widthdiff;
961 
962 #ifdef DUMPDEBUGPATH
963     if ( psfile != NULL )
964       fprintf( psfile, "%f %f t1alignedvstem\n", (stems[stemno].alx)*up,
965 	       (stems[stemno].aldx)*up);
966 #endif
967   } else {
968     stems[stemno].lbhintval = lbhintvalue;
969     stems[stemno].rthintval = rthintvalue;
970 
971     /* store grid-aligned stem values */
972     stems[stemno].aly       = stemstart + stemshift;
973     stems[stemno].aldy      = stemwidth + widthdiff;
974 
975 #ifdef DUMPDEBUGPATH
976     if ( psfile != NULL )
977       fprintf( psfile, "%f %f t1alignedhstem\n", (stems[stemno].aly)*up,
978     	       (stems[stemno].aldy)*up);
979 #endif
980   }
981   return(0);
982 
983 }
984 
985 
986 #define LEFT   1
987 #define RIGHT  2
988 #define BOTTOM 3
989 #define TOP    4
990 
991 
992 /***********************************************************************/
993 /* Find the vertical and horizontal stems that the current point       */
994 /* (x, y) may be involved in.  At most one horizontal and one vertical */
995 /* stem can apply to a single point, since there are no overlaps       */
996 /* allowed.                                                            */
997 /*   The point list updated by this function.                          */
998 /* Hints are ignored inside a DotSection.                              */
999 /***********************************************************************/
FindStems(double x,double y,double dx,double dy,double nextdx,double nextdy)1000 static void FindStems( double x, double y,
1001 		       double dx, double dy,
1002 		       double nextdx, double nextdy)
1003 {
1004   int i;
1005   int newvert, newhor;
1006   int newhorhalf, newverthalf;
1007 
1008   /* The following values will be used to decide whether a curve
1009      crosses or touches a stem in an aligned manner or not */
1010   double dtana     = 0.0;   /* tangent of pre-delta against horizontal line */
1011   double dtanb     = 0.0;   /* tangent of pre-delta against vertical line */
1012   double nextdtana = 0.0;   /* tangent of post-delta against horizontal line */
1013   double nextdtanb = 0.0;   /* tangent of post-delta against vertical line */
1014 
1015 
1016   /* setup default hinted position */
1017   ppoints[numppoints-1].ax     = ppoints[numppoints-1].x;
1018   ppoints[numppoints-1].ay     = ppoints[numppoints-1].y;
1019   if ( ppoints[numppoints-1].hinted == -1 )
1020     /* point is not to be hinted! */
1021     return;
1022   else
1023     ppoints[numppoints-1].hinted = 0;
1024 
1025   if ( InDotSection )
1026     return;
1027 
1028   if ( ProcessHints == 0 ) {
1029     return;
1030   }
1031 
1032   /* setup (absolute) tangent values and define limits that indicate nearly
1033      horizontal or nearly vertical alignment */
1034 #define NEARLYVERTICAL     0.2   /* This corresponds to about 11.3 degress deviation */
1035 #define NEARLYHORIZONTAL   0.2   /* from the ideal direction.                        */
1036   if ( dy != 0 ) {
1037     dtana = dx/dy;
1038     if ( dtanb < 0.0 )
1039       dtana = -dtana;
1040   }
1041   else
1042     dtana = NEARLYHORIZONTAL;
1043   if ( dx != 0 ) {
1044     dtanb = dy/dx;
1045     if ( dtanb < 0.0 )
1046       dtanb = -dtanb;
1047   }
1048   else
1049     dtanb = NEARLYVERTICAL;
1050   if ( nextdy != 0 ) {
1051     nextdtana = nextdx/nextdy;
1052     if ( nextdtana < 0.0 )
1053       nextdtana = -nextdtana;
1054   }
1055   else
1056     nextdtana = NEARLYHORIZONTAL;
1057   if ( nextdx != 0 ) {
1058     nextdtanb = nextdy/nextdx;
1059     if ( nextdtanb < 0.0 )
1060       nextdtanb = -nextdtanb;
1061   }
1062   else
1063     nextdtanb = NEARLYVERTICAL;
1064 
1065   newvert = newhor = -1;
1066   newhorhalf = newverthalf = -1;
1067 
1068   for (i = currstartstem; i < numstems; i++) {
1069     if (stems[i].vertical) { /* VSTEM hint */
1070       /* OK, stem is crossed in an aligned way */
1071       if ( (dtana <= NEARLYVERTICAL) || (nextdtana <= NEARLYVERTICAL)) {
1072 	if ((x >= stems[i].x ) &&
1073 	    (x <= stems[i].x+stems[i].dx )) {
1074 	  newvert = i;
1075 	  if (x < stems[i].x+stems[i].dx / 2)
1076 	    newverthalf = LEFT;
1077 	  else
1078 	    newverthalf = RIGHT;
1079 	}
1080       }
1081     }
1082     else {                 /* HSTEM hint */
1083       if ( (dtanb <= NEARLYHORIZONTAL) || (nextdtanb <= NEARLYHORIZONTAL)) {
1084 	/* OK, stem is crossed in an aligned way */
1085 	if ((y >= stems[i].y ) &&
1086 	    (y <= stems[i].y+stems[i].dy )) {
1087 	  newhor = i;
1088 	  if (y < stems[i].y+stems[i].dy / 2)
1089 	    newhorhalf = BOTTOM;
1090 	  else
1091 	    newhorhalf = TOP;
1092 	}
1093       }
1094     }
1095   }
1096 
1097   if ( newvert != -1 ) {
1098     /* mark the latest point in the point list to be v-hinted! */
1099     if ( newverthalf == LEFT ) {
1100       /* left hint */
1101       ppoints[numppoints-1].ax  += stems[newvert].lbhintval;
1102     }
1103     else {
1104        /* right hint */
1105       ppoints[numppoints-1].ax  += stems[newvert].rthintval;
1106     }
1107     ppoints[numppoints-1].hinted |= 0x01;
1108   }
1109   if ( newhor != -1 ) {
1110     /* mark the latest point in the point list to be h-hinted! */
1111     if ( newhorhalf == BOTTOM ) {
1112       /* bottom hint */
1113       ppoints[numppoints-1].ay  += stems[newhor].lbhintval;
1114     }
1115     else {
1116        /* top hint */
1117       ppoints[numppoints-1].ay  += stems[newhor].rthintval;
1118     }
1119     ppoints[numppoints-1].hinted |= 0x02;
1120   }
1121 
1122   return;
1123 
1124 }
1125 
1126 
1127 /* Type 1 internal functions */
ClearStack()1128 static int ClearStack()
1129 {
1130   Top = -1;
1131   return(0);
1132 
1133 }
1134 
Push(Num)1135 static int Push(Num)
1136         DOUBLE Num;
1137 {
1138   if (++Top < MAXSTACK) Stack[Top] = Num;
1139   else Error0i("Push: Stack full\n");
1140   return(0);
1141 
1142 }
1143 
ClearCallStack()1144 static int ClearCallStack()
1145 {
1146   CallTop = -1;
1147   return(0);
1148 }
1149 
PushCall(CurrStrP,CurrIndex,CurrKey)1150 static int PushCall(CurrStrP, CurrIndex, CurrKey)
1151   psobj *CurrStrP;
1152   int CurrIndex;
1153   unsigned short CurrKey;
1154 {
1155   if (++CallTop < MAXCALLSTACK) {
1156     CallStack[CallTop].currstrP = CurrStrP;   /* save CharString pointer */
1157     CallStack[CallTop].currindex = CurrIndex; /* save CharString index */
1158     CallStack[CallTop].currkey = CurrKey;     /* save decryption key */
1159   }
1160   else Error0i("PushCall: Stack full\n");
1161   return(0);
1162 }
1163 
PopCall(CurrStrPP,CurrIndexP,CurrKeyP)1164 static int PopCall(CurrStrPP, CurrIndexP, CurrKeyP)
1165   psobj **CurrStrPP;
1166   int *CurrIndexP;
1167   unsigned short *CurrKeyP;
1168 {
1169   if (CallTop >= 0) {
1170     *CurrStrPP = CallStack[CallTop].currstrP; /* restore CharString pointer */
1171     *CurrIndexP = CallStack[CallTop].currindex; /* restore CharString index */
1172     *CurrKeyP = CallStack[CallTop--].currkey;   /* restore decryption key */
1173   }
1174   else Error0i("PopCall: Stack empty\n");
1175   return(0);
1176 }
1177 
1178 
ClearPSFakeStack()1179 static int ClearPSFakeStack()
1180 {
1181   PSFakeTop = -1;
1182   return(0);
1183 }
1184 
1185 /* PSFakePush: Pushes a number onto the fake PostScript stack */
PSFakePush(Num)1186 static int PSFakePush(Num)
1187   DOUBLE Num;
1188 {
1189   if (++PSFakeTop < MAXPSFAKESTACK) PSFakeStack[PSFakeTop] = Num;
1190   else Error0i("PSFakePush: Stack full\n");
1191   return(0);
1192 }
1193 
1194 /* PSFakePop: Removes a number from the top of the fake PostScript stack */
PSFakePop()1195 static DOUBLE PSFakePop ()
1196 {
1197   if (PSFakeTop >= 0) return(PSFakeStack[PSFakeTop--]);
1198 
1199   else Error0d("PSFakePop : Stack empty\n");
1200 
1201   /*NOTREACHED*/
1202 }
1203 
1204 /***********************************************************************/
1205 /* Center a stem on the pixel grid -- used by HStem3 and VStem3        */
1206 /***********************************************************************/
CenterStem(edge1,edge2)1207 static struct segment *CenterStem(edge1, edge2)
1208     DOUBLE edge1;
1209     DOUBLE edge2;
1210 {
1211   int idealwidth, verticalondevice;
1212   DOUBLE leftx, lefty, rightx, righty, center, width;
1213   DOUBLE widthx, widthy;
1214   DOUBLE shift, shiftx, shifty;
1215   DOUBLE Xpixels, Ypixels;
1216   struct segment *p;
1217 
1218   p = Loc(CharSpace, edge1, 0.0);
1219   QueryLoc(p, IDENTITY, &leftx, &lefty);
1220 
1221   p = Join(p, Loc(CharSpace, edge2, 0.0));
1222   QueryLoc(p, IDENTITY, &rightx, &righty);
1223   Destroy(p);
1224 
1225   widthx = FABS(rightx - leftx);
1226   widthy = FABS(righty - lefty);
1227 
1228   if (widthy <= EPS) { /* verticalondevice hint */
1229     verticalondevice = TRUE;
1230     center = (rightx + leftx) / 2.0;
1231     width = widthx;
1232   }
1233   else if (widthx <= EPS) { /* horizontal hint */
1234     verticalondevice = FALSE;
1235     center = (righty + lefty) / 2.0;
1236     width = widthy;
1237   }
1238   else { /* neither horizontal nor verticalondevice and not oblique */
1239     return (NULL);
1240   }
1241 
1242   idealwidth = ROUND(width);
1243   if (idealwidth == 0) idealwidth = 1;
1244   if (ODD(idealwidth)) {       /* is ideal width odd? */
1245     /* center stem over pixel */
1246     shift = FLOOR(center) + 0.5 - center;
1247   }
1248   else {
1249     /* align stem on pixel boundary */
1250     shift = ROUND(center) - center;
1251   }
1252 
1253   if (verticalondevice) {
1254     shiftx = shift;
1255     shifty = 0.0;
1256   } else {
1257     shifty = shift;
1258     shiftx = 0.0;
1259   }
1260 
1261   p = Loc(IDENTITY, shiftx, shifty);
1262   QueryLoc(p, CharSpace, &Xpixels, &Ypixels);
1263   wsoffsetX = Xpixels; wsoffsetY = Ypixels;
1264   currx += wsoffsetX; curry += wsoffsetY;
1265 
1266   return (p);
1267 }
1268 
1269 /*-----------------------------------------------------------------------
1270   Decrypt - From Adobe Type 1 book page 63, with some modifications
1271 -----------------------------------------------------------------------*/
1272 #define KEY 4330 /* Initial key (seed) for CharStrings decryption */
1273 #define C1 52845 /* Multiplier for pseudo-random number generator */
1274 #define C2 22719 /* Constant for pseudo-random number generator */
1275 
1276 static unsigned short r; /* Pseudo-random sequence of keys */
1277 
Decrypt(cipher)1278 static unsigned char Decrypt(cipher)
1279 unsigned char cipher;
1280 {
1281   unsigned char plain;
1282 
1283   plain = cipher ^ (r >> 8);
1284   r = (cipher + r) * C1 + C2;
1285   return plain;
1286 }
1287 
1288 /* Get the next byte from the codestring being interpreted */
DoRead(CodeP)1289 static int DoRead(CodeP)
1290   int *CodeP;
1291 {
1292   if (strindex >= CharStringP->len) return(FALSE); /* end of string */
1293   /* We handle the non-documented Adobe convention to use lenIV=-1 to
1294      suppress charstring encryption. */
1295   if (blues->lenIV==-1) {
1296     *CodeP = (unsigned char) CharStringP->data.stringP[strindex++];
1297   }
1298   else {
1299     *CodeP = Decrypt((unsigned char) CharStringP->data.stringP[strindex++]);
1300   }
1301 
1302   return(TRUE);
1303 }
1304 
1305 /* Strip blues->lenIV bytes from CharString and update encryption key */
1306 /* (the lenIV entry in the Private dictionary specifies the number of */
1307 /* random bytes at the beginning of each CharString; default is 4)    */
StartDecrypt()1308 static void StartDecrypt()
1309 {
1310   int Code;
1311 
1312   r = KEY; /* Initial key (seed) for CharStrings decryption */
1313   for (strindex = 0; strindex < blues->lenIV;){
1314     if (!DoRead(&Code)) /* Read a byte and update decryption key */
1315       Error0v("StartDecrypt: Premature end of CharString\n");
1316   }
1317 
1318 }
1319 
1320 #undef DecodeDebug
1321 
Decode(Code)1322 static int Decode(Code)
1323   int Code;
1324 {
1325   int Code1, Code2, Code3, Code4;
1326 
1327   if (Code <= 31){                           /* Code is [0,31]    */
1328 #ifdef DecodeDebug
1329     fprintf(stderr, "Decode: Code=%d -> Command\n", Code);
1330 #endif
1331     DoCommand(Code);
1332   }
1333   else if (Code <= 246){                     /* Code is [32,246]  */
1334 #ifdef DecodeDebug
1335     fprintf(stderr, "Decode: Code=%d -> number=%f\n",
1336 	    Code, (DOUBLE)(Code-139));
1337 #endif
1338     Push((DOUBLE)(Code - 139));
1339   }
1340   else if (Code <= 250) {                   /* Code is [247,250] */
1341     if (!DoRead(&Code2)) goto ended;
1342 #ifdef DecodeDebug
1343     fprintf(stderr, "Decode: Code=%d next Code=%d -> number=%f\n",
1344 	    Code, Code2, (DOUBLE)(((Code - 247) << 8) + Code2 + 108));
1345 #endif
1346     Push((DOUBLE)(((Code - 247) << 8) + Code2 + 108));
1347   }
1348   else if (Code <= 254) {                   /* Code is [251,254] */
1349     if (!DoRead(&Code2)) goto ended;
1350 #ifdef DecodeDebug
1351     fprintf(stderr, "Decode: Code=%d, next Code=%d -> number=%f\n",
1352 	    Code, Code2, (DOUBLE)( -((Code - 251) << 8) - Code2 - 108));
1353 #endif
1354     Push((DOUBLE)( -((Code - 251) << 8) - Code2 - 108));
1355   }
1356   else {                                    /* Code is 255 */
1357     if (!DoRead(&Code1)) goto ended;
1358     if (!DoRead(&Code2)) goto ended;
1359     if (!DoRead(&Code3)) goto ended;
1360     if (!DoRead(&Code4)) goto ended;
1361 #ifdef DecodeDebug
1362     fprintf(stderr, "Decode: Code=255, Code1=%d, Code2=%d, Code3=%d, Code4=%d -> number=%f\n",
1363 	    Code1, Code2, Code3, Code4,
1364 	    (DOUBLE)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
1365 #endif
1366     Push((DOUBLE)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
1367   }
1368   return(0);
1369 
1370 ended: Error0i("Decode: Premature end of Type 1 CharString");
1371 }
1372 
1373 #undef DoCommandDebug
1374 
1375 /* Interpret a command code */
DoCommand(Code)1376 static int DoCommand(Code)
1377   int Code;
1378 {
1379   switch(Code) {
1380     case HSTEM: /* |- y dy HSTEM |- */
1381 #ifdef DoCommandDebug
1382       printf("DoCommand: HStem\n");
1383 #endif
1384       /* Vertical range of a horizontal stem zone */
1385       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1386       HStem(Stack[0], Stack[1]);
1387       ClearStack();
1388       break;
1389     case VSTEM: /* |- x dx VSTEM |- */
1390 #ifdef DoCommandDebug
1391       printf("DoCommand: VStem\n");
1392 #endif
1393       /* Horizontal range of a vertical stem zone */
1394       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1395       VStem(Stack[0], Stack[1]);
1396       ClearStack();
1397       break;
1398     case VMOVETO: /* |- dy VMOVETO |- */
1399 #ifdef DoCommandDebug
1400       printf("DoCommand: VMoveto\n");
1401 #endif
1402       /* Vertical MOVETO, equivalent to 0 dy RMOVETO */
1403       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1404       RMoveTo(0.0, Stack[0]);
1405       ClearStack();
1406       break;
1407     case RLINETO: /* |- dx dy RLINETO |- */
1408 #ifdef DoCommandDebug
1409       printf("DoCommand: RLineto\n");
1410 #endif
1411       /* Like RLINETO in PostScript */
1412       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1413       RLineTo(Stack[0], Stack[1]);
1414       ClearStack();
1415       break;
1416     case HLINETO: /* |- dx HLINETO |- */
1417 #ifdef DoCommandDebug
1418       printf("DoCommand: HLineto\n");
1419 #endif
1420       /* Horizontal LINETO, equivalent to dx 0 RLINETO */
1421       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1422       RLineTo(Stack[0], 0.0);
1423       ClearStack();
1424       break;
1425     case VLINETO: /* |- dy VLINETO |- */
1426 #ifdef DoCommandDebug
1427       printf("DoCommand: VLineto\n");
1428 #endif
1429       /* Vertical LINETO, equivalent to 0 dy RLINETO */
1430       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1431       RLineTo(0.0, Stack[0]);
1432       ClearStack();
1433       break;
1434     case RRCURVETO:
1435 #ifdef DoCommandDebug
1436       printf("DoCommand: RRCurveto\n");
1437 #endif
1438       /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
1439       /* Relative RCURVETO, equivalent to dx1 dy1 */
1440       /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
1441       /* (dy1+dy2+dy3) RCURVETO in PostScript */
1442       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1443       RRCurveTo(Stack[0], Stack[1], Stack[2], Stack[3],
1444         Stack[4], Stack[5]);
1445       ClearStack();
1446       break;
1447     case CLOSEPATH: /* - CLOSEPATH |- */
1448 #ifdef DoCommandDebug
1449       printf("DoCommand: ClosePath\n");
1450 #endif
1451       /* Closes a subpath without repositioning the */
1452       /* current point */
1453       DoClosePath();
1454       ClearStack();
1455       break;
1456     case CALLSUBR: /* subr# CALLSUBR - */
1457 #ifdef DoCommandDebug
1458       printf("DoCommand: CallSubr\n");
1459 #endif
1460       /* Calls a CharString subroutine with index */
1461       /* subr# from the Subrs array */
1462       if (Top < 0) Error1i("DoCommand: Stack low\n (Code=%d)", Code);
1463       CallSubr((int)Stack[Top--]);
1464       break;
1465     case RETURN: /* - RETURN - */
1466 #ifdef DoCommandDebug
1467       printf("DoCommand: Return\n");
1468 #endif
1469       /* Returns from a Subrs array CharString */
1470       /* subroutine called with CALLSUBR */
1471       Return();
1472       break;
1473     case ESCAPE: /* ESCAPE to two-byte command code */
1474 #ifdef DoCommandDebug
1475       printf("DoCommand: Escape to 2 Byte Code (Code=%d)\n", Code);
1476 #endif
1477       if (!DoRead(&Code)) Error0i("DoCommand: ESCAPE is last byte\n");
1478       Escape(Code);
1479       break;
1480     case HSBW: /* |- sbx wx HSBW |- */
1481 #ifdef DoCommandDebug
1482       printf("DoCommand: HSBW\n");
1483 #endif
1484       /* Set the left sidebearing point to (sbx,0), */
1485       /* set the character width vector to (wx,0). */
1486       /* Equivalent to sbx 0 wx 0 SBW.  Space */
1487       /* character should have sbx = 0 */
1488       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1489       Sbw(Stack[0], 0.0, Stack[1], 0.0);
1490       ClearStack();
1491       break;
1492     case ENDCHAR: /* - ENDCHAR |- */
1493 #ifdef DoCommandDebug
1494       printf("DoCommand: EndChar\n");
1495 #endif
1496       /* Finishes a CharString outline */
1497       EndChar();
1498       ClearStack();
1499       break;
1500     case RMOVETO: /* |- dx dy RMOVETO |- */
1501 #ifdef DoCommandDebug
1502       printf("DoCommand: RMoveto\n");
1503 #endif
1504       /* Behaves like RMOVETO in PostScript */
1505       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1506       RMoveTo(Stack[0], Stack[1]);
1507       ClearStack();
1508       break;
1509     case HMOVETO: /* |- dx HMOVETO |- */
1510 #ifdef DoCommandDebug
1511       printf("DoCommand: HMovetoUnassigned\n");
1512 #endif
1513       /* Horizontal MOVETO. Equivalent to dx 0 RMOVETO */
1514       if (Top < 0) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1515       RMoveTo(Stack[0], 0.0);
1516       ClearStack();
1517       break;
1518     case VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
1519 #ifdef DoCommandDebug
1520       printf("DoCommand: VHCurveto\n");
1521 #endif
1522       /* Vertical-Horizontal CURVETO, equivalent to */
1523       /* 0 dy1 dx2 dy2 dx3 0 RRCURVETO */
1524       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1525       RRCurveTo(0.0, Stack[0], Stack[1], Stack[2],
1526               Stack[3], 0.0);
1527       ClearStack();
1528       break;
1529     case HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
1530 #ifdef DoCommandDebug
1531       printf("DoCommand: HCurveto\n");
1532 #endif
1533       /* Horizontal-Vertical CURVETO, equivalent to */
1534       /* dx1 0 dx2 dy2 0 dy3 RRCURVETO */
1535       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1536       RRCurveTo(Stack[0], 0.0, Stack[1], Stack[2], 0.0, Stack[3]);
1537       ClearStack();
1538       break;
1539   case UNKNOWN_15:
1540     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1541     ClearStack();
1542     break;
1543     default: /* Unassigned command code */
1544 #ifdef DoCommandDebug
1545       printf("DoCommand: Unassigned\n");
1546 #endif
1547       ClearStack();
1548       Error1i("DoCommand: Unassigned code %d\n", Code);
1549   }
1550   return(0);
1551 
1552 }
1553 
Escape(Code)1554 static int Escape(Code)
1555   int Code;
1556 {
1557   int i, Num;
1558   struct segment *p;
1559 
1560   switch(Code) {
1561     case DOTSECTION: /* - DOTSECTION |- */
1562       /* Brackets an outline section for the dots in */
1563       /* letters such as "i", "j", and "!". */
1564       DotSection();
1565       ClearStack();
1566       break;
1567     case VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
1568       /* Declares the horizontal ranges of three */
1569       /* vertical stem zones between x0 and x0+dx0, */
1570       /* x1 and x1+dx1, and x2 and x2+dx2. */
1571       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1572       if (!wsset && ProcessHints) {
1573         /* Shift the whole character so that the middle stem is centered. */
1574         p = CenterStem(Stack[2] + sidebearingX, Stack[3]);
1575         path = Join(path, p);
1576         wsset = 1;
1577       }
1578 
1579       VStem(Stack[0], Stack[1]);
1580       VStem(Stack[2], Stack[3]);
1581       VStem(Stack[4], Stack[5]);
1582       ClearStack();
1583       break;
1584     case HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
1585       /* Declares the vertical ranges of three hori- */
1586       /* zontal stem zones between y0 and y0+dy0, */
1587       /* y1 and y1+dy1, and y2 and y2+dy2. */
1588       if (Top < 5) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1589       HStem(Stack[0], Stack[1]);
1590       HStem(Stack[2], Stack[3]);
1591       HStem(Stack[4], Stack[5]);
1592       ClearStack();
1593       break;
1594     case SEAC: /* |- asb adx ady bchar achar SEAC |- */
1595       /* Standard Encoding Accented Character. */
1596       if (Top < 4) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1597       Seac(Stack[0], Stack[1], Stack[2],
1598         (unsigned char) Stack[3],
1599         (unsigned char) Stack[4]);
1600       ClearStack();
1601       break;
1602     case SBW: /* |- sbx sby wx wy SBW |- */
1603       /* Set the left sidebearing point to (sbx,sby), */
1604       /* set the character width vector to (wx,wy). */
1605       if (Top < 3) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1606       Sbw(Stack[0], Stack[1], Stack[2], Stack[3]);
1607       ClearStack();
1608       break;
1609     case DIV: /* num1 num2 DIV quotient */
1610       /* Behaves like DIV in the PostScript language */
1611       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1612       Stack[Top-1] = Div(Stack[Top-1], Stack[Top]);
1613       Top--;
1614       break;
1615     case CALLOTHERSUBR:
1616       /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
1617       /* Make calls on the PostScript interpreter */
1618       if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1619       Num = Stack[Top-1];
1620       if (Top < Num+1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1621       for (i = 0; i < Num; i++) PSFakePush(Stack[Top - i - 2]);
1622       Top -= Num + 2;
1623       CallOtherSubr((int)Stack[Top + Num + 2]);
1624       break;
1625     case POP: /* - POP number */
1626       /* Removes a number from the top of the */
1627       /* PostScript interpreter stack and pushes it */
1628       /* onto the Type 1 BuildChar operand stack */
1629       Push(PSFakePop());
1630       break;
1631   case SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
1632     /* Sets the current point to (x,y) in absolute */
1633     /* character space coordinates without per- */
1634     /* forming a CharString MOVETO command */
1635     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1636     SetCurrentPoint(Stack[0], Stack[1]);
1637     ClearStack();
1638     break;
1639   case UNKNOWN_15:
1640     if (Top < 1) Error1i("DoCommand: Stack low (Code=%d)\n", Code);
1641     ClearStack();
1642     break;
1643   default: /* Unassigned escape code command */
1644     ClearStack();
1645     Error1i("Escape: Unassigned code %d\n", Code);
1646   }
1647   return(0);
1648 
1649 }
1650 
1651 /* |- y dy HSTEM |- */
1652 /* Declares the vertical range of a horizontal stem zone */
1653 /* between coordinates y and y + dy */
1654 /* y is relative to the left sidebearing point */
HStem(y,dy)1655 static int HStem(y, dy)
1656   DOUBLE y, dy;
1657 {
1658   IfTrace2((FontDebug), "Hstem %f %f\n", y, dy);
1659   if (ProcessHints) {
1660     if (numstems >= MAXSTEMS) Error0i("HStem: Too many hints\n");
1661     if (dy < 0.0) {y += dy; dy = -dy;}
1662     stems[numstems].vertical = FALSE;
1663     stems[numstems].x = 0.0;
1664     stems[numstems].y = sidebearingY + y + wsoffsetY;
1665     stems[numstems].dx = 0.0;
1666     stems[numstems].dy = dy;
1667     ComputeStem(numstems);
1668     numstems++;
1669   }
1670   return(0);
1671 }
1672 
1673 /* |- x dx VSTEM |- */
1674 /* Declares the horizontal range of a vertical stem zone */
1675 /* between coordinates x and x + dx */
1676 /* x is relative to the left sidebearing point */
VStem(x,dx)1677 static int VStem(x, dx)
1678   DOUBLE x, dx;
1679 {
1680   IfTrace2((FontDebug), "Vstem %f %f\n", x, dx);
1681   if (ProcessHints) {
1682     if (numstems >= MAXSTEMS) Error0i("VStem: Too many hints\n");
1683     if (dx < 0.0) {x += dx; dx = -dx;}
1684     stems[numstems].vertical = TRUE;
1685     stems[numstems].x = sidebearingX + x + wsoffsetX;
1686     stems[numstems].y = 0.0;
1687     stems[numstems].dx = dx;
1688     stems[numstems].dy = 0.0;
1689     ComputeStem(numstems);
1690     numstems++;
1691   }
1692   return(0);
1693 }
1694 
1695 /* |- dx dy RLINETO |- */
1696 /* Behaves like RLINETO in PostScript */
RLineTo(dx,dy)1697 static int RLineTo(dx, dy)
1698   DOUBLE dx, dy;
1699 {
1700   long pindex = 0;
1701 
1702   /* compute hinting for previous segment! */
1703   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx, dy);
1704 
1705   /* Allocate a new path point and pre-setup data */
1706   pindex = nextPPoint();
1707   ppoints[pindex].x       = currx + dx;
1708   ppoints[pindex].y       = curry + dy;
1709   ppoints[pindex].ax      = ppoints[pindex].x;
1710   ppoints[pindex].ay      = ppoints[pindex].y;
1711   ppoints[pindex].type    = PPOINT_LINE;
1712   ppoints[pindex].hinted  = 0;
1713 
1714   /* update ideal position */
1715   currx                  += dx;
1716   curry                  += dy;
1717 
1718   return(0);
1719 }
1720 
1721 /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
1722 /* Relative RCURVETO, equivalent to dx1 dy1 */
1723 /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
1724 /* (dy1+dy2+dy3) RCURVETO in PostScript */
RRCurveTo(dx1,dy1,dx2,dy2,dx3,dy3)1725 static int RRCurveTo(dx1, dy1, dx2, dy2, dx3, dy3)
1726   DOUBLE dx1, dy1, dx2, dy2, dx3, dy3;
1727 {
1728   long pindex = 0;
1729 
1730   /* compute hinting for previous point! */
1731   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx1, dy1);
1732 
1733   /* Allocate three new path points and pre-setup data */
1734   pindex = nextPPoint();
1735   ppoints[pindex].x       = currx + dx1;
1736   ppoints[pindex].y       = curry + dy1;
1737   ppoints[pindex].ax      = ppoints[pindex].x;
1738   ppoints[pindex].ay      = ppoints[pindex].y;
1739   ppoints[pindex].type    = PPOINT_BEZIER_B;
1740   ppoints[pindex].hinted  = 0;
1741 
1742   /* update ideal position */
1743   currx                  += dx1;
1744   curry                  += dy1;
1745 
1746   /* compute hinting for previous point! */
1747   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx2, dy2);
1748 
1749   pindex = nextPPoint();
1750   ppoints[pindex].x       = currx + dx2;
1751   ppoints[pindex].y       = curry + dy2;
1752   ppoints[pindex].ax      = ppoints[pindex].x;
1753   ppoints[pindex].ay      = ppoints[pindex].y;
1754   ppoints[pindex].type    = PPOINT_BEZIER_C;
1755   ppoints[pindex].hinted  = 0;
1756 
1757   /* update ideal position */
1758   currx                  += dx2;
1759   curry                  += dy2;
1760 
1761   /* compute hinting for previous point! */
1762   FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y, dx3, dy3);
1763 
1764   pindex = nextPPoint();
1765   ppoints[pindex].x       = currx + dx3;
1766   ppoints[pindex].y       = curry + dy3;
1767   ppoints[pindex].ax      = ppoints[pindex].x;
1768   ppoints[pindex].ay      = ppoints[pindex].y;
1769   ppoints[pindex].type    = PPOINT_BEZIER_D;
1770   ppoints[pindex].hinted  = 0;
1771 
1772   /* update ideal position */
1773   currx                  += dx3;
1774   curry                  += dy3;
1775 
1776   return(0);
1777 }
1778 
1779 /* - CLOSEPATH |- */
1780 /* Closes a subpath WITHOUT repositioning the */
1781 /* current point */
DoClosePath()1782 static int DoClosePath()
1783 {
1784   long pindex = 0;
1785   long i = 0;
1786   long tmpind;
1787   double deltax = 0.0;
1788   double deltay = 0.0;
1789 
1790   /* If this ClosePath command together with the starting point of this
1791      path completes to a segment aligned to a stem, we would miss
1792      hinting for this point. --> Check and explicitly care for this! */
1793   /* 1. Step back in the point list to the last moveto-point */
1794   i = numppoints - 1;
1795   while ( (i > 0) && (ppoints[i].type != PPOINT_MOVE ) ) {
1796     --i;
1797   }
1798 
1799   /* 2. Re-hint starting point and hint current point */
1800   if ( ppoints[i].type == PPOINT_MOVE) {
1801     deltax = ppoints[i].x - ppoints[numppoints-1].x;
1802     deltay = ppoints[i].y - ppoints[numppoints-1].y;
1803 
1804     /* save nummppoints and reset to move point */
1805     tmpind = numppoints;
1806     numppoints = i + 1;
1807 
1808     /* re-hint starting point of current subpath (uses the value of numppoints!) */
1809     FindStems( ppoints[i].x, ppoints[i].y, deltax, deltay,
1810 	       ppoints[i+1].x-ppoints[i].x, ppoints[i+1].y-ppoints[i].y);
1811 
1812     /* restore numppoints and setup hinting for current point */
1813     numppoints = tmpind;
1814     FindStems( currx, curry, currx-ppoints[numppoints-2].x, curry-ppoints[numppoints-2].y,
1815 	       deltax, deltay);
1816   }
1817 
1818   /* Allocate a new path point and pre-setup data */
1819   pindex = nextPPoint();
1820   ppoints[pindex].x       = currx;
1821   ppoints[pindex].y       = curry;
1822   ppoints[pindex].ax      = ppoints[pindex-1].x;
1823   ppoints[pindex].ay      = ppoints[pindex-1].y;
1824   ppoints[pindex].type    = PPOINT_CLOSEPATH;
1825   ppoints[pindex].hinted  = 0;
1826 
1827   return(0);
1828 }
1829 
1830 /* subr# CALLSUBR - */
1831 /* Calls a CharString subroutine with index */
1832 /* subr# from the Subrs array */
CallSubr(subrno)1833 static int CallSubr(subrno)
1834   int subrno;
1835 {
1836   IfTrace2((FontDebug), "CallSubr %d (CallStackSize=%d)\n", subrno, CallTop);
1837   if ((subrno < 0) || (subrno >= SubrsP->len))
1838     Error0i("CallSubr: subrno out of range\n");
1839   PushCall(CharStringP, strindex, r);
1840   CharStringP = &SubrsP->data.arrayP[subrno];
1841   StartDecrypt();
1842   return(0);
1843 }
1844 
1845 /* - RETURN - */
1846 /* Returns from a Subrs array CharString */
1847 /* subroutine called with CALLSUBR */
Return()1848 static int Return()
1849 {
1850   IfTrace0((FontDebug), "Return\n");
1851   PopCall(&CharStringP, &strindex, &r);
1852   return(0);
1853 }
1854 
1855 /* - ENDCHAR |- */
1856 /* Finishes a CharString outline */
1857 /* Executes SETCHACHEDEVICE using a bounding box */
1858 /* it computes directly from the character outline */
1859 /* and using the width information acquired from a previous */
1860 /* HSBW or SBW.  It then calls a special version of FILL */
1861 /* or STROKE depending on the value of PaintType in the */
1862 /* font dictionary */
EndChar()1863 static int EndChar()
1864 {
1865   long pindex = 0;
1866 
1867   IfTrace0((FontDebug), "EndChar\n");
1868 
1869   /* There is no need to compute and set bounding box for
1870      the cache, since XIMAGER does that on the fly. */
1871 
1872   /* Allocate a new path point and pre-setup data.
1873      Note: For this special case, we use the variables that usually
1874      store hinted coordinates for the escapement of the character.
1875      It is required in handleCurrentSegment().
1876   */
1877   pindex = nextPPoint();
1878   ppoints[pindex].x       = currx;
1879   ppoints[pindex].y       = curry;
1880   ppoints[pindex].ax      = escapementX;
1881   ppoints[pindex].ay      = escapementY;
1882   ppoints[pindex].type    = PPOINT_ENDCHAR;
1883   ppoints[pindex].hinted  = -1;
1884 
1885   return(0);
1886 
1887 }
1888 
1889 /* |- dx dy RMOVETO |- */
1890 /* Behaves like RMOVETO in PostScript */
RMoveTo(dx,dy)1891 static int RMoveTo(dx,dy)
1892   DOUBLE dx,dy;
1893 {
1894   long pindex = 0;
1895 
1896   /* Compute hinting for this path point! */
1897   if ( numppoints == 1 ) {
1898     /* Since RMoveTo for this case starts a new path segment
1899        (flex-constructs have already been handled), the current
1900        point is hinted here only taking the next point into account,
1901        but not the previous. Later on, in DoClosePath(), we'll step
1902        back to this point and the position might be rehinted. */
1903     FindStems( currx, curry, 0, 0, dx, dy);
1904   }
1905   else {
1906     FindStems( currx, curry, ppoints[numppoints-2].x, ppoints[numppoints-2].y, dx, dy);
1907   }
1908 
1909 
1910 
1911   /* Allocate a new path point and pre-setup data */
1912   pindex = nextPPoint();
1913   ppoints[pindex].x       = currx + dx;
1914   ppoints[pindex].y       = curry + dy;
1915   ppoints[pindex].ax      = ppoints[pindex].x;
1916   ppoints[pindex].ay      = ppoints[pindex].y;
1917   ppoints[pindex].type    = PPOINT_MOVE;
1918   ppoints[pindex].hinted  = 0;
1919 
1920   /* update ideal position */
1921   currx                  += dx;
1922   curry                  += dy;
1923 
1924   return 0;
1925 }
1926 
1927 /* - DOTSECTION |- */
1928 /* Brackets an outline section for the dots in */
1929 /* letters such as "i", "j", and "!". */
DotSection()1930 static int DotSection()
1931 {
1932   IfTrace0((FontDebug), "DotSection\n");
1933   InDotSection = !InDotSection;
1934   return(0);
1935 }
1936 
1937 /* |- asb adx ady bchar achar SEAC |- */
1938 /* Standard Encoding Accented Character. */
Seac(asb,adx,ady,bchar,achar)1939 static int Seac(asb, adx, ady, bchar, achar)
1940   DOUBLE asb, adx, ady;
1941   unsigned char bchar, achar;
1942 {
1943   int Code;
1944   long pindex = 0;
1945 
1946   isseac      = 1;
1947   seacaccent  = achar;
1948   seacbase    = bchar;
1949 
1950   IfTrace4((FontDebug), "SEAC %f %f %f %d ", asb, adx, ady, bchar);
1951   IfTrace1((FontDebug), "%d\n", achar);
1952 
1953   /* Move adx - asb, ady over and up from base char's sbpoint. */
1954   /* (We use adx - asb to counteract the accents sb shift.) */
1955   /* The variables accentoffsetX/Y modify sidebearingX/Y in Sbw(). */
1956   /* Note that these incorporate the base character's sidebearing shift by */
1957   /* using the current sidebearingX, Y values. */
1958   accentoffsetX = adx - asb;
1959   accentoffsetY = ady;
1960 
1961   /* Set path = NULL to avoid complaints from Sbw(). */
1962   path = NULL;
1963 
1964   /* Go find the CharString for the accent's code via an upcall */
1965   CharStringP = GetType1CharString(Environment, achar);
1966   if (CharStringP == NULL) {
1967      Error1i("Invalid accent ('%03o) in SEAC\n", achar);
1968   }
1969   StartDecrypt();
1970 
1971   ClearStack();
1972   ClearPSFakeStack();
1973   ClearCallStack();
1974 
1975   for (;;) {
1976     if (!DoRead(&Code)) break;
1977     Decode(Code);
1978     if (errflag) return(0);
1979   }
1980 
1981   /* Allocate a new path point. Data in this case is not relevant
1982      in handleSegment(), we merely insert a snap() in order to return
1983      to origin of the accent char. */
1984   pindex = nextPPoint();
1985   ppoints[pindex].x       = accentoffsetX;
1986   ppoints[pindex].y       = accentoffsetY;
1987   ppoints[pindex].ax      = accentoffsetX;
1988   ppoints[pindex].ay      = accentoffsetY;
1989   ppoints[pindex].type    = PPOINT_SEAC;
1990   ppoints[pindex].hinted  = 0;
1991 
1992   /* We must reset these to null now. */
1993   accentoffsetX = accentoffsetY = 0;
1994 
1995   /* go find the CharString for the base char's code via an upcall */
1996   CharStringP = GetType1CharString(Environment, bchar);
1997   StartDecrypt();
1998 
1999   ClearStack();
2000   ClearPSFakeStack();
2001   ClearCallStack();
2002 
2003   InitStems();
2004 
2005   for (;;) {
2006     if (!DoRead(&Code)) break;
2007     Decode(Code);
2008     if (errflag) return(0);
2009   }
2010 
2011   return(0);
2012 }
2013 
2014 
2015 /* |- sbx sby wx wy SBW |- */
2016 /* Set the left sidebearing point to (sbx,sby), */
2017 /* set the character width vector to (wx,wy). */
Sbw(sbx,sby,wx,wy)2018 static int Sbw(sbx, sby, wx, wy)
2019   DOUBLE sbx, sby, wx, wy;
2020 {
2021   long pindex = 0;
2022 
2023 
2024   IfTrace4((FontDebug), "SBW %f %f %f %f\n", sbx, sby, wx, wy);
2025 
2026   escapementX = wx; /* Character width vector */
2027   escapementY = wy;
2028 
2029   /* Sidebearing values are sbx, sby args, plus accent offset from Seac(). */
2030   sidebearingX = sbx + accentoffsetX;
2031   sidebearingY = sby + accentoffsetY;
2032 
2033   currx = sidebearingX;
2034   curry = sidebearingY;
2035   /*
2036   path = Join(path, Loc(CharSpace, sidebearingX, sidebearingY));
2037   if (ProcessHints) {
2038     hcurrx = sidebearingX;
2039     hcurry = sidebearingY;
2040   }
2041   */
2042 
2043   /* Allocate a path point and setup.
2044      Note: In this special case, we store the char escapement in the members
2045            ax and ay. They are required in handleCurrentSegment(). Hinting
2046 	   is not required for SBW, anyhow!
2047   */
2048   pindex = nextPPoint();
2049   ppoints[pindex].x       = currx;
2050   ppoints[pindex].y       = curry;
2051   ppoints[pindex].ax      = wx;
2052   ppoints[pindex].ay      = wy;
2053   ppoints[pindex].type    = PPOINT_SBW;
2054   ppoints[pindex].hinted  = -1; /* indicate that point is not to be hinted */
2055 
2056   return(0);
2057 }
2058 
2059  /* num1 num2 DIV quotient */
2060 /* Behaves like DIV in the PostScript language */
Div(num1,num2)2061 static DOUBLE Div(num1, num2)
2062   DOUBLE num1, num2;
2063 {
2064   IfTrace2((FontDebug), "Div %f %f\n", num1, num2);
2065   return(num1 / num2);
2066 }
2067 
2068 /*
2069   The following four subroutines (FlxProc, FlxProc1, FlxProc2, and
2070   HintReplace) are C versions of the OtherSubrs Programs, which were
2071   were published in the Adobe Type 1 Font Format book.
2072 
2073   The Flex outline fragment is described by
2074     c1: (x0, y0) = c3: (x0, yshrink(y0)) or (xshrink(x0), y0)
2075      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
2076      "  (x2, y2) - reference point
2077     c2: (x0, y0) = c4: (x0, yshrink(y0)) or (xshrink(x0), y0)
2078      "  (x1, y1) =  "  (x1, yshrink(y1)) or (xshrink(x1), y1)
2079      "  (x2, y2) =  "  (x2, y2), rightmost endpoint
2080     c3: (x0, y0) - control point, 1st Bezier curve
2081      "  (x1, y1) - control point,      -"-
2082      "  (x2, y2) - end point,          -"-
2083     c4: (x0, y0) - control point, 2nd Bezier curve
2084      "  (x1, y1) - control point,      -"-
2085      "  (x2, y2) - end point,          -"-
2086     ep: (epY, epX) - final endpoint (should be same as c4: (x2, y2))
2087     idmin - minimum Flex height (1/100 pixel) at which to render curves
2088 */
2089 
2090 #define dtransform(dxusr,dyusr,dxdev,dydev) { \
2091   register struct segment *point = Loc(CharSpace, dxusr, dyusr); \
2092   QueryLoc(point, IDENTITY, dxdev, dydev); \
2093   Destroy(point); \
2094 }
2095 
2096 #define itransform(xdev,ydev,xusr,yusr) { \
2097   register struct segment *point = Loc(IDENTITY, xdev, ydev); \
2098   QueryLoc(point, CharSpace, xusr, yusr); \
2099   Destroy(point); \
2100 }
2101 
2102 #define transform(xusr,yusr,xdev,ydev) dtransform(xusr,yusr,xdev,ydev)
2103 
2104 #define PaintType (0)
2105 
2106 #define xshrink(x) ((x - c4x2) * shrink +c4x2)
2107 #define yshrink(y) ((y - c4y2) * shrink +c4y2)
2108 
2109 #define PickCoords(flag) \
2110   if (flag) { /* Pick "shrunk" coordinates */ \
2111     x0 = c1x0; y0 = c1y0; \
2112     x1 = c1x1; y1 = c1y1; \
2113     x2 = c1x2; y2 = c1y2; \
2114     x3 = c2x0; y3 = c2y0; \
2115     x4 = c2x1; y4 = c2y1; \
2116     x5 = c2x2; y5 = c2y2; \
2117   } else { /* Pick original coordinates */ \
2118     x0 = c3x0; y0 = c3y0; \
2119     x1 = c3x1; y1 = c3y1; \
2120     x2 = c3x2; y2 = c3y2; \
2121     x3 = c4x0; y3 = c4y0; \
2122     x4 = c4x1; y4 = c4y1; \
2123     x5 = c4x2; y5 = c4y2; \
2124   }
2125 
2126 /* FlxProc() = OtherSubrs[0]; Main part of Flex          */
2127 /*   Calling sequence: 'idmin epX epY 3 0 callothersubr' */
2128 /*   Computes Flex values, and renders the Flex path,    */
2129 /*   and returns (leaves) ending coordinates on stack    */
FlxProc(c1x2,c1y2,c3x0,c3y0,c3x1,c3y1,c3x2,c3y2,c4x0,c4y0,c4x1,c4y1,c4x2,c4y2,epY,epX,idmin)2130 static void FlxProc(c1x2, c1y2, c3x0, c3y0, c3x1, c3y1, c3x2, c3y2,
2131              c4x0, c4y0, c4x1, c4y1, c4x2, c4y2, epY, epX, idmin)
2132   DOUBLE c1x2, c1y2;
2133   DOUBLE c3x0, c3y0, c3x1, c3y1, c3x2, c3y2;
2134   DOUBLE c4x0, c4y0, c4x1, c4y1, c4x2, c4y2;
2135   DOUBLE epX, epY;
2136   int idmin;
2137 {
2138   DOUBLE dmin;
2139   DOUBLE c1x0, c1y0, c1x1, c1y1;
2140   DOUBLE c2x0, c2y0, c2x1, c2y1, c2x2, c2y2;
2141   char yflag;
2142   DOUBLE x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5;
2143   DOUBLE cxx, cyx, cxy, cyy; /* Transformation matrix */
2144   int flipXY;
2145   DOUBLE x, y;
2146   DOUBLE erosion = 1; /* Device parameter */
2147     /* Erosion may have different value specified in 'internaldict' */
2148   DOUBLE shrink;
2149   DOUBLE dX, dY;
2150   char erode;
2151   DOUBLE eShift;
2152   DOUBLE cx, cy;
2153   DOUBLE ex, ey;
2154 
2155 
2156   /* Our PPOINT list now contains 7 moveto commands which
2157      are about to be consumed by the Flex mechanism. --> Remove these
2158      seven elements (their values already reside on the PSFakeStack!)
2159      and approriately restore the current accumulated position. */
2160   numppoints -= 7;
2161   currx = ppoints[numppoints-1].x;
2162   curry = ppoints[numppoints-1].y;
2163 
2164   if (ProcessHints) {
2165     dmin = TYPE1_ABS(idmin) / 100.0; /* Minimum Flex height in pixels */
2166 
2167     c2x2 = c4x2; c2y2 = c4y2; /* Point c2 = c4 */
2168 
2169     yflag = FABS(c1y2 - c3y2) > FABS(c1x2 - c3x2); /* Flex horizontal? */
2170 
2171     QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
2172 
2173     if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
2174       flipXY = -1; /* Char on side */
2175     else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
2176       flipXY = 1; /* Char upright */
2177     else
2178       flipXY = 0; /* Char at angle */
2179 
2180     if (yflag) { /* Flex horizontal */
2181       if (flipXY == 0 || c3y2 == c4y2) { /* Char at angle or Flex height = 0 */
2182         PickCoords(FALSE); /* Pick original control points */
2183       } else {
2184         shrink = FABS((c1y2 - c4y2) / (c3y2 - c4y2)); /* Slope */
2185 
2186         c1x0 = c3x0; c1y0 = yshrink(c3y0);
2187         c1x1 = c3x1; c1y1 = yshrink(c3y1);
2188         c2x0 = c4x0; c2y0 = yshrink(c4y0);
2189         c2x1 = c4x1; c2y1 = yshrink(c4y1);
2190 
2191         dtransform(0.0, ROUND(c3y2-c1y2), &x, &y); /* Flex height in pixels */
2192         dY = FABS((flipXY == 1) ? y : x);
2193         PickCoords(dY < dmin); /* If Flex small, pick 'shrunk' control points */
2194 
2195         if (FABS(y2 - c1y2) > 0.001) { /* Flex 'non-zero'? */
2196           transform(c1x2, c1y2, &x, &y);
2197 
2198           if (flipXY == 1) {
2199             cx = x; cy = y;
2200           } else {
2201             cx = y; cy = x;
2202           }
2203 
2204           dtransform(0.0, ROUND(y2-c1y2), &x, &y);
2205           dY = (flipXY == 1) ? y : x;
2206           if (ROUND(dY) != 0)
2207             dY = ROUND(dY);
2208           else
2209             dY = (dY < 0) ? -1 : 1;
2210 
2211           erode = PaintType != 2 && erosion >= 0.5;
2212           if (erode)
2213             cy -= 0.5;
2214           ey = cy + dY;
2215           ey = CEIL(ey) - ey + FLOOR(ey);
2216           if (erode)
2217             ey += 0.5;
2218 
2219           if (flipXY == 1) {
2220             itransform(cx, ey, &x, &y);
2221           } else {
2222             itransform(ey, cx, &x, &y);
2223           }
2224 
2225           eShift = y - y2;
2226           y1 += eShift;
2227           y2 += eShift;
2228           y3 += eShift;
2229         }
2230       }
2231     } else { /* Flex vertical */
2232       if (flipXY == 0 || c3x2 == c4x2) { /* Char at angle or Flex height = 0 */
2233         PickCoords(FALSE); /* Pick original control points */
2234       } else {
2235         shrink = FABS((c1x2 - c4x2) / (c3x2 - c4x2)); /* Slope */
2236 
2237         c1x0 = xshrink(c3x0); c1y0 = c3y0;
2238         c1x1 = xshrink(c3x1); c1y1 = c3y1;
2239         c2x0 = xshrink(c4x0); c2y0 = c4y0;
2240         c2x1 = xshrink(c4x1); c2y1 = c4y1;
2241 
2242         dtransform(ROUND(c3x2 - c1x2), 0.0, &x, &y); /* Flex height in pixels */
2243         dX = FABS((flipXY == -1) ? y : x);
2244         PickCoords(dX < dmin); /* If Flex small, pick 'shrunk' control points */
2245 
2246         if (FABS(x2 - c1x2) > 0.001) {
2247           transform(c1x2, c1y2, &x, &y);
2248           if (flipXY == -1) {
2249             cx = y; cy = x;
2250           } else {
2251             cx = x; cy = y;
2252           }
2253 
2254           dtransform(ROUND(x2-c1x2), 0.0, &x, &y);
2255           dX = (flipXY == -1) ? y : x;
2256           if (ROUND(dX) != 0)
2257             dX = ROUND(dX);
2258           else
2259             dX = (dX < 0) ? -1 : 1;
2260 
2261           erode = PaintType != 2 && erosion >= 0.5;
2262           if (erode)
2263             cx -= 0.5;
2264           ex = cx + dX;
2265           ex = CEIL(ex) - ex + FLOOR(ex);
2266           if (erode)
2267             ex += 0.5;
2268 
2269           if (flipXY == -1) {
2270             itransform(cy, ex, &x, &y);
2271           } else {
2272             itransform(ex, cy, &x, &y);
2273           }
2274 
2275           eShift = x - x2;
2276           x1 += eShift;
2277           x2 += eShift;
2278           x3 += eShift;
2279         }
2280       }
2281     }
2282 
2283     if (x2 == x5 || y2 == y5) {
2284       RLineTo( x5 - currx, y5 - curry); \
2285     } else {
2286       RRCurveTo( x0 - currx, y0 - curry,
2287 		 x1 - x0, y1 - y0,
2288 		 x2 - x1,
2289 		 y2 - y1);
2290       RRCurveTo( x3 - currx, y3 - curry,
2291 		 x4 - x3, y4 - y3,
2292 		 x5 - x4, y5 - y4);
2293     }
2294   } else { /* ProcessHints is off */
2295     PickCoords(FALSE); /* Pick original control points */
2296     RRCurveTo( x0 - currx, y0 - curry,
2297 	       x1 - x0, y1 - y0,
2298 	       x2 - x1,
2299 	       y2 - y1);
2300     RRCurveTo( x3 - currx, y3 - curry,
2301 	       x4 - x3, y4 - y3,
2302 	       x5 - x4, y5 - y4);
2303   }
2304 
2305   PSFakePush(epY);
2306   PSFakePush(epX);
2307 }
2308 
2309 /* FlxProc1() = OtherSubrs[1]; Part of Flex            */
2310 /*   Calling sequence: '0 1 callothersubr'             */
2311 /*   Saves and clears path, then restores currentpoint */
FlxProc1()2312 static void FlxProc1()
2313 {
2314   /* Since we are now building the path point list,
2315      there's nothing to do here! */
2316   return;
2317 }
2318 
2319 /* FlxProc2() = OtherSubrs[2]; Part of Flex */
2320 /*   Calling sequence: '0 2 callothersubr'  */
2321 /*   Returns currentpoint on stack          */
FlxProc2()2322 static void FlxProc2()
2323 {
2324   /* Push CurrentPoint on fake PostScript stack */
2325   PSFakePush( ppoints[numppoints-1].x);
2326   PSFakePush( ppoints[numppoints-1].y);
2327 }
2328 
2329 /* HintReplace() = OtherSubrs[3]; Hint Replacement            */
2330 /*   Calling sequence: 'subr# 1 3 callothersubr pop callsubr' */
2331 /*   Reinitializes stem hint structure                        */
HintReplace()2332 static void HintReplace()
2333 {
2334   /* Effectively retire the current stems, but keep them around for */
2335   /* revhint use in case we are in a stem when we replace hints. */
2336   currstartstem = numstems;
2337 
2338   /* 'subr#' is left on PostScript stack (for 'pop callsubr') */
2339 }
2340 
2341 /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
2342 /* Make calls on the PostScript interpreter (or call equivalent C code) */
2343 /* NOTE: The n arguments have been pushed on the fake PostScript stack */
CallOtherSubr(othersubrno)2344 static int CallOtherSubr(othersubrno)
2345   int othersubrno;
2346 {
2347   IfTrace1((FontDebug), "CallOtherSubr %d\n", othersubrno);
2348 
2349   switch(othersubrno) {
2350   case 0: /* OtherSubrs[0]; Main part of Flex */
2351     if (PSFakeTop < 16) Error0i("CallOtherSubr: PSFakeStack low");
2352     ClearPSFakeStack();
2353     FlxProc(
2354 	    PSFakeStack[0],  PSFakeStack[1],  PSFakeStack[2],  PSFakeStack[3],
2355 	    PSFakeStack[4],  PSFakeStack[5],  PSFakeStack[6],  PSFakeStack[7],
2356 	    PSFakeStack[8],  PSFakeStack[9],  PSFakeStack[10], PSFakeStack[11],
2357 	    PSFakeStack[12], PSFakeStack[13], PSFakeStack[14], PSFakeStack[15],
2358 	    (int) PSFakeStack[16]
2359 	    );
2360     break;
2361   case 1: /* OtherSubrs[1]; Part of Flex */
2362     FlxProc1();
2363     break;
2364   case 2: /* OtherSubrs[2]; Part of Flex */
2365     FlxProc2();
2366     break;
2367   case 3: /* OtherSubrs[3]; Hint Replacement */
2368     HintReplace();
2369     break;
2370   case 12: /* OtherSubrs[12]: Counter Control Hinting */
2371     /* We do nothing, because later OtherSubrs[13] will
2372        remove data from the PS-stack */
2373     break;
2374   case 13:
2375     /* We pop all elements from PSFakeStack. They may either have been left
2376        from previous calls to OtherSubr #12 or have been pushed for this
2377        call */
2378     ClearPSFakeStack();
2379   break;
2380   default: { /* call OtherSubrs[4] or higher if PostScript is present */
2381 
2382   }
2383   }
2384   return(0);
2385 }
2386 
2387 /* |- x y SETCURRENTPOINT |- */
2388 /* Sets the current point to (x,y) in absolute */
2389 /* character space coordinates without per- */
2390 /* forming a CharString MOVETO command */
SetCurrentPoint(x,y)2391 static int SetCurrentPoint(x, y)
2392   DOUBLE x, y;
2393 {
2394   IfTrace2((FontDebug), "SetCurrentPoint %f %f\n", x, y);
2395 
2396   currx = x;
2397   curry = y;
2398   return(0);
2399 }
2400 
2401 /* The Type1Char routine for use by PostScript. */
2402 /************************************************/
Type1Char(psfont * env,struct XYspace * S,psobj * charstrP,psobj * subrsP,psobj * osubrsP,struct blues_struct * bluesP,int * modeP,char * charname,float strokewidth,int decodeonly)2403 struct xobject *Type1Char(psfont *env, struct XYspace *S,
2404 			  psobj *charstrP, psobj *subrsP,
2405 			  psobj *osubrsP,
2406 			  struct blues_struct *bluesP,
2407 			  int *modeP, char *charname,
2408 			  float strokewidth,
2409 			  int decodeonly)
2410 {
2411   int Code;
2412   long i = 0;
2413 
2414   double xp, yp;
2415 #ifdef DUMPDEBUGPATH
2416   char fnbuf[128];
2417 #endif
2418   struct segment* p;
2419 
2420   /* Reset SEAC querying variables */
2421   isseac     = 0;
2422   seacbase   = 0;
2423   seacaccent = 0;
2424 
2425   /* Reset point list */
2426   ppoints          = NULL;
2427   numppoints       = 0;
2428   numppointchunks  = 0;
2429 
2430   /* disable hinting for stroking */
2431   if ( strokewidth != 0.0f )
2432     ProcessHints = 0;
2433 
2434   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 )
2435     ProcessHints = 0;
2436 
2437   path    = NULL;
2438   apath   = NULL;
2439   errflag = FALSE;
2440 
2441   if ( S == NULL ) {
2442     S=(struct XYspace *) IDENTITY;
2443   }
2444 
2445   /* Make parameters available to all Type1 routines */
2446   currentchar=charname;
2447   Environment = (char *)env;
2448   CharSpace = S; /* used when creating path elements */
2449   CharStringP = charstrP;
2450   SubrsP = subrsP;
2451   OtherSubrsP = osubrsP;
2452   ModeP = modeP;
2453 
2454   blues = bluesP;
2455 
2456   if ( decodeonly == 0 ) {
2457     QuerySpace( S, &scxx, &scyx, &scxy, &scyy); /* Transformation matrix */
2458     p = ILoc( S, 1, 0);
2459     QueryLoc(p, IDENTITY, &xp, &yp);
2460     up = FABS(xp);
2461 
2462     size = scxx * 1000.0;
2463 #ifdef DUMPDEBUGPATH
2464     sprintf( fnbuf, "t1dump_%s_%f.eps", charname, size);
2465     psfile = fopen( fnbuf, "wb");
2466     if ( psfile != NULL ) {
2467       PSDumpProlog( psfile);
2468       fprintf( psfile, "T1LibDict begin\n\ngsave\n%f t1SetupSize\nt1PreparePage\n", size);
2469     }
2470 #endif
2471   }
2472 
2473   /* compute the alignment zones */
2474   SetRasterFlags();
2475   ComputeAlignmentZones();
2476   StartDecrypt();
2477   ClearStack();
2478   ClearPSFakeStack();
2479   ClearCallStack();
2480   InitStems();
2481 
2482   /* reset variables */
2483   currx = curry = 0.0;
2484   hcurrx = hcurry = 0.0;
2485   escapementX = escapementY = 0;
2486   sidebearingX = sidebearingY = 0;
2487   accentoffsetX = accentoffsetY = 0;
2488   wsoffsetX = wsoffsetY = 0;           /* No shift to preserve whitspace. */
2489   wsset = 0;                           /* wsoffsetX,Y haven't been set yet. */
2490 
2491   for (;;) {
2492     if (!DoRead(&Code)) break;
2493     Decode(Code);
2494     if (errflag) break;
2495   }
2496 
2497   if ( decodeonly != 0 ) {
2498     /* OK, character description is now on stack, finish ... */
2499     return NULL;
2500   }
2501 
2502   /* We now have a point list in absolute charspace coordinates. Adjust
2503      non-hinted points to suppress hinting artifacts and generate path. */
2504   for ( i=0; i<numppoints; i++ ) {
2505     if ( ppoints[i].type == PPOINT_BEZIER_D)
2506       adjustBezier( i);
2507   }
2508   /* Create path elements */
2509 #if defined(DUMPDEBUGPATH) & defined(DUMPDEBUGPATHBOTH)
2510   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 0 ) {
2511     /* For this special debug case, we generate both a filled and a stroked
2512        path!. */
2513       createStrokePath( strokewidth, SUBPATH_CLOSED);
2514       createFillPath();
2515   }
2516   else if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 ) {
2517     /* PaintType = 1 indicates stroked font. If strokewidth is 0.0f,
2518        we stroke using the font's internal strokewidth. Otherwise, the
2519        user supplied value is used. */
2520     if ( strokewidth != 0.0f )
2521       createStrokePath( strokewidth, SUBPATH_OPEN);
2522     else
2523       createStrokePath( env->fontInfoP[STROKEWIDTH].value.data.real, SUBPATH_OPEN);
2524   }
2525 #else
2526   if ( env->fontInfoP[PAINTTYPE].value.data.integer == 0 ) {
2527     /* PaintType = 0 indicates filled font. Hence, a strokewidth value
2528        other than 0.0 indicates the character is to be stroked instead
2529        of to be filled. */
2530     if ( strokewidth != 0.0f )
2531       createStrokePath( strokewidth, SUBPATH_CLOSED);
2532     else
2533       createFillPath();
2534   }
2535   else if ( env->fontInfoP[PAINTTYPE].value.data.integer == 1 ) {
2536     /* PaintType = 1 indicates stroked font. If strokewidth is 0.0f,
2537        we stroke using the font's internal strokewidth. Otherwise, the
2538        user supplied value is used. */
2539     if ( strokewidth != 0.0f )
2540       createStrokePath( strokewidth, SUBPATH_OPEN);
2541     else
2542       createStrokePath( env->fontInfoP[STROKEWIDTH].value.data.real, SUBPATH_OPEN);
2543   }
2544 #endif
2545 
2546   /* check and handle accented char */
2547   if ( apath != NULL ) {
2548     path = Join( apath, path);
2549   }
2550 
2551   /* Report a possible error: */
2552   *modeP=errflag;
2553 
2554   /* Clean up if an error has occurred */
2555   if (errflag) {
2556     if (path != NULL) {
2557       Destroy(path); /* Reclaim storage */
2558       path = NULL;   /* Indicate that character could not be built */
2559     }
2560   }
2561 
2562 #ifdef DUMPDEBUGPATH
2563   if ( psfile != NULL ) {
2564     PSDumpEpilog( psfile);
2565     fclose( psfile);
2566     psfile = 0;
2567   }
2568 #endif
2569 
2570   /* Cleanup ppoints */
2571   if ( ppoints != NULL ) {
2572     free( ppoints);
2573     ppoints = NULL;
2574     numppoints = 0;
2575   }
2576 
2577   return((struct xobject *) path);
2578 }
2579 
2580 
2581 /* We add a function to implement underlining in Type1-Routines. */
Type1Line(psfont * env,struct XYspace * S,float line_position,float line_thickness,float line_length,float strokewidth)2582 struct xobject *Type1Line(psfont *env, struct XYspace *S,
2583 			  float line_position,
2584 			  float line_thickness,
2585 			  float line_length,
2586 			  float strokewidth)
2587 {
2588 
2589   /* Reset point list */
2590   ppoints          = NULL;
2591   numppoints       = 0;
2592   numppointchunks  = 0;
2593 
2594   path    = NULL;
2595   apath   = NULL;
2596   errflag = FALSE;
2597 
2598 
2599   /* Make parameters available to all Type1 routines */
2600   Environment = (char *)env;
2601   CharSpace = S; /* used when creating path elements */
2602 
2603   currx = curry = 0;
2604   escapementX = escapementY = 0;
2605   sidebearingX = sidebearingY = 0;
2606   /* We have to initialize the stems-structures because
2607      the lineto commands refer to them! */
2608   SetRasterFlags();
2609   InitStems();
2610 
2611   /* Draw Character */
2612   Sbw(-1.0*line_length, 0.0, 0.0, 0.0);
2613   RMoveTo( 0.0,  (double) (line_position+0.5*line_thickness));
2614   RLineTo( 0.0, (double) -line_thickness);
2615   RLineTo( (double) line_length, 0.0);
2616   RLineTo( 0.0, (double) line_thickness);
2617   DoClosePath();
2618   EndChar();
2619 
2620   /* Create path */
2621   if ( strokewidth != 0.0f )
2622     createStrokePath( strokewidth, SUBPATH_CLOSED);
2623   else
2624     createFillPath();
2625 
2626   /* Cleanup ppoints */
2627   if ( ppoints != NULL ) {
2628     free( ppoints);
2629     ppoints = NULL;
2630   }
2631 
2632   return((struct xobject *)path);
2633 
2634 }
2635 
2636 
2637 /* Adjust the points of a given cubic Bezier Spline so that the
2638    geometric relation of points B and C to A and D remain
2639    qualitatively the same. This reduces hinting artifacts
2640    at low resolutions.
2641 */
adjustBezier(long pindex)2642 static void adjustBezier( long pindex)
2643 {
2644   double deltax  = 0.0;      /* x distance between point A and D */
2645   double deltay  = 0.0;      /* y distance between point A and D */
2646   double adeltax = 0.0;      /* x distance between hinted point A and D */
2647   double adeltay = 0.0;      /* y distance between hinted point A and D */
2648 
2649   deltax  = ppoints[pindex].x - ppoints[pindex-3].x;
2650   deltay  = ppoints[pindex].y - ppoints[pindex-3].y;
2651   adeltax = ppoints[pindex].ax - ppoints[pindex-3].ax;
2652   adeltay = ppoints[pindex].ay - ppoints[pindex-3].ay;
2653 
2654   if ( deltax == 0 || deltay == 0 )
2655     return;
2656 
2657   ppoints[pindex-1].ax = ppoints[pindex-3].ax +
2658     (adeltax / deltax * (ppoints[pindex-1].x - ppoints[pindex-3].x));
2659   ppoints[pindex-1].ay = ppoints[pindex-3].ay +
2660     (adeltay / deltay * (ppoints[pindex-1].y - ppoints[pindex-3].y));
2661   ppoints[pindex-2].ax = ppoints[pindex-3].ax +
2662     (adeltax / deltax * (ppoints[pindex-2].x - ppoints[pindex-3].x));
2663   ppoints[pindex-2].ay = ppoints[pindex-3].ay +
2664     (adeltay / deltay * (ppoints[pindex-2].y - ppoints[pindex-3].y));
2665 
2666   return;
2667 
2668 }
2669 
2670 
2671 
2672 /* This function actually generates path segments. It is called
2673    after all hinting and adjustments have been performed.
2674 */
handleCurrentSegment(long pindex)2675 static void handleCurrentSegment( long pindex)
2676 {
2677   struct segment* B;
2678   struct segment* C;
2679   struct segment* D;
2680   struct segment* tmpseg;
2681   double dx1;
2682   double dy1;
2683   double dx2;
2684   double dy2;
2685   double dx3;
2686   double dy3;
2687   double adx1;
2688   double ady1;
2689   double adx2;
2690   double ady2;
2691   double adx3;
2692   double ady3;
2693 
2694 
2695   /* handle the different segment types in a switch-statement */
2696   switch ( ppoints[pindex].type ) {
2697 
2698   case PPOINT_MOVE:
2699     /* handle a move segment */
2700     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
2701     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
2702     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
2703     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
2704 
2705 #ifdef DUMPDEBUGPATH
2706     if ( psfile != NULL )
2707       fprintf( psfile, "%f %f t1rmoveto %% pindex = %ld\n", dx1*up, dy1*up, pindex);
2708 #endif
2709     if ( ProcessHints ) {
2710       IfTrace2((FontDebug), "RMoveTo(h) %f %f\n", adx1, ady1);
2711       B = Loc(CharSpace, adx1, ady1);
2712 #ifdef DUMPDEBUGPATH
2713       if ( psfile != NULL ) {
2714 	fprintf( psfile, "%f %f t1hintedrmoveto %% pindex = %ld\n", adx1*up, ady1*up, pindex);
2715       }
2716 #endif
2717     }
2718     else {
2719       IfTrace2((FontDebug), "RMoveTo %f %f\n", dx1, dy1);
2720       B = Loc(CharSpace, dx1, dy1);
2721     }
2722     path = Join(path, B);
2723     break;
2724 
2725 
2726   case PPOINT_LINE:
2727     /* handle a line segment */
2728     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
2729     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
2730     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
2731     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
2732 
2733 #ifdef DUMPDEBUGPATH
2734     if ( psfile != NULL )
2735       fprintf( psfile, "%f %f t1rlineto %% pindex = %ld\n", dx1*up, dy1*up, pindex);
2736 #endif
2737     if ( ProcessHints ) {
2738       IfTrace2((FontDebug), "RLineTo(h) %f %f\n", adx1, ady1);
2739       B = Loc(CharSpace, adx1, ady1);
2740 #ifdef DUMPDEBUGPATH
2741       if ( psfile != NULL ) {
2742 	fprintf( psfile, "%f %f t1hintedrlineto %% pindex = %ld\n", adx1*up, ady1*up, pindex);
2743       }
2744 #endif
2745     }
2746     else {
2747       IfTrace2((FontDebug), "RLineTo %f %f\n", dx1, dy1);
2748       B = Loc(CharSpace, dx1, dy1);
2749     }
2750     path = Join(path, Line(B));
2751     break;
2752 
2753 
2754   case PPOINT_BEZIER_B:
2755     /* handle a bezier segment (given by this and the following points) */
2756     dx1  = ppoints[pindex].x - ppoints[pindex-1].x;
2757     dy1  = ppoints[pindex].y - ppoints[pindex-1].y;
2758     adx1 = ppoints[pindex].ax - ppoints[pindex-1].ax;
2759     ady1 = ppoints[pindex].ay - ppoints[pindex-1].ay;
2760     dx2  = ppoints[pindex+1].x - ppoints[pindex].x;
2761     dy2  = ppoints[pindex+1].y - ppoints[pindex].y;
2762     adx2 = ppoints[pindex+1].ax - ppoints[pindex].ax;
2763     ady2 = ppoints[pindex+1].ay - ppoints[pindex].ay;
2764     dx3  = ppoints[pindex+2].x - ppoints[pindex+1].x;
2765     dy3  = ppoints[pindex+2].y - ppoints[pindex+1].y;
2766     adx3 = ppoints[pindex+2].ax - ppoints[pindex+1].ax;
2767     ady3 = ppoints[pindex+2].ay - ppoints[pindex+1].ay;
2768 
2769 
2770 #ifdef DUMPDEBUGPATH
2771     if ( psfile != NULL )
2772       fprintf( psfile, "%f %f %f %f %f %f t1rrcurveto %% pindex = %ld\n",
2773 	       dx1*up, dy1*up,
2774 	       dx2*up, dy2*up,
2775 	       dx3*up, dy3*up,
2776 	       pindex);
2777 #endif
2778     if (ProcessHints) {
2779       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
2780 	       adx1, ady1, adx2, ady2);
2781       IfTrace2((FontDebug), "%f %f\n", adx3, ady3);
2782       B = Loc(CharSpace, adx1, ady1);
2783       C = Loc(CharSpace, adx2, ady2);
2784       D = Loc(CharSpace, adx3, ady3);
2785 #ifdef DUMPDEBUGPATH
2786       if ( psfile != NULL ) {
2787 	fprintf( psfile, "%f %f %f %f %f %f t1hintedrrcurveto %% pindex = %ld\n",
2788 		 adx1*up, ady1*up,
2789 		 adx2*up, ady2*up,
2790 		 adx3*up, ady3*up,
2791 		 pindex);
2792       }
2793 #endif
2794     }
2795     else {
2796       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
2797 	       dx1, dy1, dx2, dy2);
2798       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
2799       B = Loc(CharSpace, dx1, dy1);
2800       C = Loc(CharSpace, dx2, dy2);
2801       D = Loc(CharSpace, dx3, dy3);
2802     }
2803 
2804     /* Since XIMAGER is not completely relative, */
2805     /* we need to add up the delta values */
2806     C = Join(C, (struct segment *)Dup(B));
2807     D = Join(D, (struct segment *)Dup(C));
2808     path = Join(path, (struct segment *)Bezier(B, C, D));
2809     break;
2810 
2811 
2812   case PPOINT_SBW:
2813 #ifdef DUMPDEBUGPATH
2814   if ( psfile != NULL )
2815     fprintf( psfile, "%f %f %f %f t1sbw %% pindex = %ld\n",
2816 	     ppoints[pindex].x*up, ppoints[pindex].y*up,   /* sidebearings */
2817 	     ppoints[pindex].ax*up, ppoints[pindex].ay*up,  /* escapements  */
2818 	     pindex
2819 	     );
2820 #endif
2821     path = Join(path, Loc(CharSpace, ppoints[pindex].x, ppoints[pindex].y));
2822     break;
2823 
2824 
2825   case PPOINT_CLOSEPATH:
2826     IfTrace0((FontDebug), "DoClosePath\n");
2827 #ifdef DUMPDEBUGPATH
2828     if ( psfile != NULL ) {
2829       fprintf( psfile, "t1closepath %% pindex = %ld\n", pindex);
2830       if ( ProcessHints )
2831 	fprintf( psfile, "t1hintedclosepath %% pindex = %ld\n", pindex);
2832     }
2833 #endif
2834 
2835     tmpseg = Phantom(path);
2836     path = ClosePath(path);
2837     path = Join(Snap(path), tmpseg);
2838     break;
2839 
2840 
2841   case PPOINT_ENDCHAR:
2842     /* Perform a Closepath just in case the command was left out */
2843     path = ClosePath(path);
2844 
2845     /* Set character width / escapement. It is stored in the vars for
2846        hinted coordinates. */
2847     path = Join(Snap(path), Loc(CharSpace, ppoints[pindex].ax, ppoints[pindex].ay));
2848 
2849 #ifdef DUMPDEBUGPATH
2850     if ( psfile != NULL )
2851       fprintf( psfile, "t1FinishPage\ngrestore %% pindex = %ld\n", pindex);
2852 #endif
2853     break;
2854 
2855 
2856   case PPOINT_SEAC:
2857     /* return to origin of accent */
2858     apath = Snap(path);
2859     /* reset path to NULL */
2860     path  = NULL;
2861     break;
2862 
2863 
2864   default:
2865     /* We must be at the beginning of the path, that is, there is
2866        nothing to do! */
2867     ;
2868   }
2869 
2870   return;
2871 
2872 }
2873 
2874 
2875 #ifdef DUMPDEBUGPATH
PSDumpProlog(FILE * fp)2876 static void PSDumpProlog( FILE* fp)
2877 {
2878 #include "t1chardump"
2879 }
2880 
2881 
PSDumpEpilog(FILE * fp)2882 static void PSDumpEpilog( FILE* fp)
2883 {
2884   fputs( "\nend\n", fp);
2885 }
2886 
2887 #endif /* #ifdef DUMPDEBUGPATH */
2888 
2889 
2890 
2891 /* Take the accumulated path point list and construct the path that is
2892    to be filled. */
createFillPath(void)2893 static void createFillPath( void)
2894 {
2895   long i;
2896 
2897   for ( i=0; i<numppoints; i++ ) {
2898     handleCurrentSegment( i);
2899   }
2900   return;
2901 }
2902 
2903 
2904 /* Take the accumulated path point list and construct a path so that the
2905    character appears as a stroked outline, where the virtual pen has a diameter
2906    of strokewidth (measured in big points [bp]). This function works analogously
2907    to PostScripts charpath operator. */
createStrokePath(double strokewidth,int subpathclosed)2908 static void createStrokePath( double strokewidth, int subpathclosed)
2909 {
2910   long pindex   = 0;
2911   long startind = 0;
2912   long stopind  = 0;
2913 
2914 
2915   /* For each subpath in the path list (some sub path is closed!), we construct
2916      one path interior and one path exterior to the path under consideration in
2917      a way, that the resulting thick curve has a thickness of strokewidth. */
2918 
2919 #ifdef DEBUG_OUTLINE_SURROUNDING
2920   fprintf( stderr, "\ncreateStrokePath(strokewidth=%f, subpathclosed=%d): Searching partial paths ...\n\n",
2921 	   strokewidth, subpathclosed);
2922 #endif
2923   if ( subpathclosed == 0 ) {
2924     /* We have a stroked font */
2925     /* loop through all subpaths */
2926     while ( pindex < numppoints ) {
2927       /* First, handle any non-subpath commands. */
2928       if ( handleNonSubPathSegments( pindex) != 0 ) {
2929 #ifdef DEBUG_OUTLINE_SURROUNDING
2930 	fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
2931 		 ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
2932 #endif
2933 	++pindex;
2934 	continue;
2935       }
2936 
2937       if ( ppoints[pindex].type == PPOINT_LINE ||
2938 	   ppoints[pindex].type == PPOINT_BEZIER_B ) {
2939 	if ( ppoints[pindex-1].type == PPOINT_MOVE ) {
2940 	  /* A line or curve segment following a move segment indicates a
2941 	     new subpath. */
2942 	  startind = pindex - 1;
2943 	  while ( (pindex < numppoints) &&
2944 		  (ppoints[pindex].type != PPOINT_CLOSEPATH) &&
2945 		  (ppoints[pindex].type != PPOINT_MOVE) &&
2946 		  (ppoints[pindex].type != PPOINT_ENDCHAR)
2947 		  ) {
2948 #ifdef DEBUG_OUTLINE_SURROUNDING
2949 	    fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
2950 		   ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
2951 #endif
2952 	    ++pindex;
2953 	  }
2954 	  if ( (ppoints[pindex].type == PPOINT_ENDCHAR) ||  /* for outline fonts */
2955 	       (ppoints[pindex].type == PPOINT_MOVE)          /* for stroked fonts */
2956 	       ) {
2957 #ifdef DEBUG_OUTLINE_SURROUNDING
2958 	    fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
2959 		   ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
2960 #endif
2961 	    stopind = --pindex;
2962 #ifdef DEBUG_OUTLINE_SURROUNDING
2963 	    fprintf( stderr, "Found subpath from index %ld to %ld inclusively\n", startind, stopind);
2964 #endif
2965 	    /* We have found a subpath defined by the path points indexed by
2966 	       the interval from startind to stopind. */
2967 	    createClosedStrokeSubPath( startind, stopind, strokewidth, subpathclosed);
2968 	  }
2969 	}
2970       }
2971       ++pindex;
2972     }
2973   }
2974   else {
2975     /* We have a filled font */
2976     /* loop through all subpaths */
2977     while ( pindex < numppoints ) {
2978       /* First, handle any non-subpath commands. */
2979       if ( handleNonSubPathSegments( pindex) != 0 ) {
2980 #ifdef DEBUG_OUTLINE_SURROUNDING
2981 	fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
2982 	       ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
2983 #endif
2984 	++pindex;
2985 	continue;
2986       }
2987 
2988       if ( ppoints[pindex].type == PPOINT_LINE ||
2989 	   ppoints[pindex].type == PPOINT_BEZIER_B ) {
2990 	if ( ppoints[pindex-1].type == PPOINT_MOVE ) {
2991 	  /* A line or curve segment following a move segment indicates a
2992 	     new subpath. */
2993 	  startind = --pindex;
2994 	  while ( (pindex < numppoints) &&
2995 		  (ppoints[pindex].type != PPOINT_CLOSEPATH)
2996 		  ) {
2997 #ifdef DEBUG_OUTLINE_SURROUNDING
2998 	    fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
2999 		   ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
3000 #endif
3001 	    ++pindex;
3002 	  }
3003 	  if ( ppoints[pindex].type == PPOINT_CLOSEPATH ) {
3004 #ifdef DEBUG_OUTLINE_SURROUNDING
3005 	    fprintf( stderr, "PPoint %ld: (%f,%f), Type=%s\n", pindex,
3006 		   ppoints[pindex].x, ppoints[pindex].y, pptypes[ppoints[pindex].type]);
3007 #endif
3008 	    stopind = pindex;
3009 #ifdef DEBUG_OUTLINE_SURROUNDING
3010 	    fprintf( stderr, "Found subpath from index %ld to %ld inclusively\n", startind, stopind);
3011 #endif
3012 	    /* We have found a subpath defined by the path points indexed by
3013 	       the interval from startind to stopind. */
3014 	    createClosedStrokeSubPath( startind, stopind, strokewidth, subpathclosed);
3015 	  }
3016 	}
3017       }
3018       ++pindex;
3019     }
3020   }
3021 
3022   return;
3023 
3024 }
3025 
3026 
3027 
3028 /* Create two closed paths that surround the the current subpath of the
3029    charpath in a centered fashion. */
createClosedStrokeSubPath(long startind,long stopind,double strokewidth,int subpathclosed)3030 static void createClosedStrokeSubPath( long startind, long stopind,
3031 				       double strokewidth, int subpathclosed)
3032 {
3033   long i;
3034   long inext;
3035   long iprev;
3036   long ip = 0;
3037   long in = 0;
3038   long segstartind;
3039   long segendind;
3040 
3041   long lastind     = 0; /* Index of last point whose location is different from first
3042 			   point. After this point there may follow an explicit line-
3043 			   or curveto to the starting point and also the ClosePath-point
3044 			   may be and usually is identical to the starting point. */
3045 
3046   double dx1;
3047   double dy1;
3048   double dx2;
3049   double dy2;
3050   double dx3;
3051   double dy3;
3052 
3053   struct segment* B;
3054   struct segment* C;
3055   struct segment* D;
3056   struct segment* tmpseg;
3057 
3058   int type;
3059 
3060 
3061   /* The ClosePath operator is somewhat problematic, because it adds a point
3062      to the defining points of a path, without actually having a distance to
3063      the previous or the next point. This causes problems with the distance
3064      handling. As a remedy, we check whether ClosePath is located at the first
3065      point or the last point of the path. In the latter case, ClosePath causes
3066      an additional line segment. */
3067   if ( (ppoints[stopind].x == ppoints[startind].x) &&
3068        (ppoints[stopind].y == ppoints[startind].y)
3069        ) {
3070     closepathatfirst = 1;
3071   }
3072   else {
3073     closepathatfirst = 0;
3074   }
3075 
3076 #ifdef DEBUG_OUTLINE_SURROUNDING
3077   if ( closepathatfirst == 1 ) {
3078     fprintf( stderr, "createClosedStrokeSubPath(): Starting up, ClosePath is at first PPoint (does not cause a line segment) ...\n");
3079   }
3080   else {
3081     fprintf( stderr, "createClosedStrokeSubPath(): Starting up, ClosePath is not at first PPoint (causes a closing line segment) ...\n");
3082   }
3083 #endif
3084 
3085 
3086   /* For each path point in the list, we have to define a set of points, to the
3087      left and to the right of the current curve. The positions of these
3088      new points depend on the coordinates of the previous path point, the current
3089      path and the next path point. */
3090 
3091   /* For the computations, the distance from the start and end points of curves
3092      and lines to the neighbouring points is required. We start by calculating
3093      these and by filling in the path point entries dist2prev and dist2next for
3094      the respective points. */
3095 #ifdef DEBUG_OUTLINE_SURROUNDING
3096   fprintf( stderr, "Computing geometric distances between path points ...\n");
3097 #endif
3098   lastind = computeDistances( startind, stopind, subpathclosed);
3099 #ifdef DEBUG_OUTLINE_SURROUNDING
3100   fprintf( stderr, "startind=%ld, lastind=%ld, stopind=%ld\n", startind, lastind, stopind);
3101 #endif
3102 
3103 
3104   /********************************************************************************
3105    ********************************************************************************
3106    *****
3107    ***** Path point transformation
3108    *****
3109    ********************************************************************************
3110    ********************************************************************************/
3111 
3112   /* Next we step through the path points of the current subpath and compute the
3113      points' transformations. From these newly computed points,
3114      the path is constructed. */
3115 #ifdef DEBUG_OUTLINE_SURROUNDING
3116   fprintf( stderr, "Computing geometric transformation points and resulting intersection points for right path ...\n\n");
3117 #endif
3118   for ( i=startind; i<=lastind; ) {
3119     /* Be aware of cycling! */
3120     if ( i == startind ) {
3121       iprev = lastind;
3122       inext = i + 1;
3123     }
3124     else if ( i == lastind ) {
3125       iprev = i - 1;
3126       inext = startind;
3127     }
3128     else {
3129       iprev = i - 1;
3130       inext = i + 1;
3131     }
3132 
3133 
3134     switch ( ppoints[i].type ) {
3135     case PPOINT_MOVE:
3136       /* The first segment always is of type PPOINT_MOVE. It is defined by the first,
3137 	 the second and the last point. */
3138       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
3139 
3140       /* Compute one point which results from prolongating the linked segment and
3141 	 and computing the intersection. The result values are stored in dxres,
3142 	 dyres. */
3143       if ( subpathclosed == 0 ) {
3144 	/* open subpath --> stroked font */
3145 	if ( i == startind ) {
3146 	  intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
3147 	}
3148 	else if ( i == lastind ) {
3149 	  intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
3150 	}
3151 	else {
3152 	  intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3153 	}
3154       }
3155       else {
3156 	intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3157       }
3158 
3159 #ifdef DEBUG_OUTLINE_SURROUNDING
3160       fprintf( stderr, "\nCurrent PathPoint: PP %ld (%s): (%f,%f), shape=%s;\n", i, pptypes[ppoints[i].type],
3161 	       ppoints[i].x, ppoints[i].y, ppshapes[ppoints[i].shape]);
3162       fprintf( stderr, "    RightPath: prev (%f,%f); next (%f,%f); res (%f,%f)\n",
3163 	       ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
3164 	       ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr,
3165 	       ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir);
3166       fprintf( stderr, "    LeftPath:  prev (%f,%f); next (%f,%f); res (%f,%f)\n\n",
3167 	       ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
3168 	       ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr,
3169 	       ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
3170 #endif
3171 
3172       break;
3173 
3174 
3175     case PPOINT_LINE:
3176       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
3177       if ( subpathclosed == 0 ) {
3178 	/* open subpath --> stroked font */
3179 	if ( i == startind )
3180 	  intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
3181 	else if ( i == lastind )
3182 	  intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
3183 	else
3184 	  intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3185       }
3186       else {
3187 	intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3188       }
3189 
3190 #ifdef DEBUG_OUTLINE_SURROUNDING
3191       fprintf( stderr, "\nCurrent PathPoint: PP %ld (%s): (%f,%f), shape=%s;\n", i, pptypes[ppoints[i].type],
3192 	       ppoints[i].x, ppoints[i].y, ppshapes[ppoints[i].shape]);
3193       fprintf( stderr, "    RightPath: prev (%f,%f); next (%f,%f); res (%f,%f)\n",
3194 	       ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
3195 	       ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr,
3196 	       ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir);
3197       fprintf( stderr, "    LeftPath:  prev (%f,%f); next (%f,%f); res (%f,%f)\n\n",
3198 	       ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
3199 	       ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr,
3200 	       ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
3201 #endif
3202 
3203       break;
3204 
3205 
3206     case PPOINT_BEZIER_B:
3207       break;
3208 
3209     case PPOINT_BEZIER_C:
3210       break;
3211 
3212     case PPOINT_BEZIER_D:
3213       transformOnCurvePathPoint( strokewidth, iprev, i, inext);
3214       if ( subpathclosed == 0 ) {
3215 	/* open subpath --> stroked font */
3216 	if ( i == startind )
3217 	  intersectRight( i, 0.5*strokewidth, INTERSECT_NEXT);
3218 	else if ( i == lastind )
3219 	  intersectRight( i, 0.5*strokewidth, INTERSECT_PREVIOUS);
3220 	else
3221 	  intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3222       }
3223       else {
3224 	intersectRight( i, 0.5*strokewidth, INTERSECT_BOTH);
3225       }
3226 
3227 #ifdef DEBUG_OUTLINE_SURROUNDING
3228       fprintf( stderr, "\nCurrent PathPoint: PP %ld (%s): (%f,%f), shape=%s;\n", i, pptypes[ppoints[i].type],
3229 	       ppoints[i].x, ppoints[i].y, ppshapes[ppoints[i].shape]);
3230       fprintf( stderr, "    RightPath: prev (%f,%f); next (%f,%f); res (%f,%f)\n",
3231 	       ppoints[i].x+ppoints[i].dxpr, ppoints[i].y+ppoints[i].dypr,
3232 	       ppoints[i].x+ppoints[i].dxnr, ppoints[i].y+ppoints[i].dynr,
3233 	       ppoints[i].x+ppoints[i].dxir, ppoints[i].y+ppoints[i].dyir);
3234       fprintf( stderr, "    LeftPath:  prev (%f,%f); next (%f,%f); res (%f,%f)\n\n",
3235 	       ppoints[i].x-ppoints[i].dxpr, ppoints[i].y-ppoints[i].dypr,
3236 	       ppoints[i].x-ppoints[i].dxnr, ppoints[i].y-ppoints[i].dynr,
3237 	       ppoints[i].x-ppoints[i].dxir, ppoints[i].y-ppoints[i].dyir);
3238 #endif
3239 
3240       /* transform the preceding two offCurve points */
3241       transformOffCurvePathPoint( strokewidth, i-2);
3242 
3243       break;
3244 
3245     case PPOINT_CLOSEPATH:
3246 
3247       break;
3248 
3249     default:
3250       break;
3251     }
3252     ++i;
3253   }
3254 
3255   /* copy the shift values from starting point to ending points that
3256      have not yet been handled */
3257   for ( ; i<=stopind; i++) {
3258     ppoints[i].dxpr      = ppoints[startind].dxpr;
3259     ppoints[i].dypr      = ppoints[startind].dypr;
3260     ppoints[i].dxnr      = ppoints[startind].dxnr;
3261     ppoints[i].dynr      = ppoints[startind].dynr;
3262     ppoints[i].dxir      = ppoints[startind].dxir;
3263     ppoints[i].dyir      = ppoints[startind].dyir;
3264     ppoints[i].dist2prev = ppoints[startind].dist2prev;
3265     ppoints[i].dist2next = ppoints[startind].dist2next;
3266     if ( ppoints[i].type == PPOINT_BEZIER_D ) {
3267       transformOffCurvePathPoint( strokewidth, i-2);
3268     }
3269     ppoints[i].shape     = ppoints[startind].shape;
3270   }
3271 
3272 
3273 #ifdef DEBUG_OUTLINE_SURROUNDING
3274 	fprintf( stderr, "\nTransformation of PathPoints finished. Starting to construct paths ...\n\n");
3275 #endif
3276 
3277   /* We now have computed the resulting shift values for each path point of the current
3278      subpath's right path. The values for the left path follow by negation.
3279      The path is still to be build!
3280   */
3281 
3282   /********************************************************************************
3283    ********************************************************************************
3284    *****
3285    ***** Construction of right path
3286    *****
3287    ********************************************************************************
3288    ********************************************************************************/
3289 
3290   /* The leading move segment is treated separately. First check from which
3291      point the leading Moveto was called. This is safe even in cases where
3292      multiple moveto appear in a series. */
3293   i = startind;
3294   while ( ppoints[i].type == PPOINT_MOVE )
3295     --i;
3296   dx1  = ppoints[startind].x - (ppoints[i].x);
3297   dy1  = ppoints[startind].y - (ppoints[i].y);
3298   /* If the first node in the subpath is not concave, we may directly jump
3299      to the intersection right path point. Otherwise, we remain at the onCurve
3300      point because later, prolongation will happen. */
3301   if ( ppoints[startind].shape != CURVE_CONCAVE ) {
3302     dx1  += ppoints[startind].dxir;
3303     dy1  += ppoints[startind].dyir;
3304   }
3305 
3306 #ifdef DUMPDEBUGPATH
3307   if ( psfile != NULL )
3308     fprintf( psfile, "%f %f t1srmoveto %% pindex = %ld\n", dx1*up, dy1*up, startind);
3309 #endif
3310   B = Loc(CharSpace, dx1, dy1);
3311   path = Join(path, B);
3312 
3313 
3314   /* Now, handle the remaining path in a loop */
3315   for ( i=startind+1; i<=stopind; ) {
3316     switch ( ppoints[i].type ) {
3317     case PPOINT_LINE:
3318       /* handle a line segment */
3319 
3320       /* 1. Check and handle starting node */
3321       linkNode( i-1, PATH_START, PATH_RIGHT);
3322 
3323       /* 2. Draw ideal isolated line segment */
3324 #ifdef DEBUG_OUTLINE_SURROUNDING
3325 	fprintf( stderr, "RP:  Line from point %ld to %ld\n", i-1, i);
3326 #endif
3327       dx1  = ppoints[i].x + ppoints[i].dxpr - (ppoints[i-1].x + ppoints[i-1].dxnr);
3328       dy1  = ppoints[i].y + ppoints[i].dypr - (ppoints[i-1].y + ppoints[i-1].dynr);
3329 #ifdef DUMPDEBUGPATH
3330       if ( psfile != NULL )
3331 	fprintf( psfile, "%f %f t1srlineto %% pindex = %ld\n", dx1*up, dy1*up, i);
3332 #endif
3333       B = Loc(CharSpace, dx1, dy1);
3334       path = Join(path, Line(B));
3335 
3336       /* 3. Check and handle ending node */
3337       linkNode( i, PATH_END, PATH_RIGHT);
3338 
3339       break;
3340 
3341     case PPOINT_BEZIER_B:
3342       break;
3343     case PPOINT_BEZIER_C:
3344       break;
3345     case PPOINT_BEZIER_D:
3346       /* handle a bezier segment (given by this and the previous 3 path points)! */
3347 
3348       /* 1. Check and handle starting node */
3349       linkNode( i-3, PATH_START, PATH_RIGHT);
3350 
3351       /* 2. Draw curve based on ideal point locations */
3352 #ifdef DEBUG_OUTLINE_SURROUNDING
3353       fprintf( stderr, "RP:  Curve from PP %ld to PP %ld to PP %ld to PP %ld\n",
3354 	       i-3, i-2, i-1, i);
3355 #endif
3356       dx1  = ppoints[i-2].x + ppoints[i-2].dxir - (ppoints[i-3].x + ppoints[i-3].dxnr);
3357       dy1  = ppoints[i-2].y + ppoints[i-2].dyir - (ppoints[i-3].y + ppoints[i-3].dynr);
3358       dx2  = ppoints[i-1].x + ppoints[i-1].dxir - (ppoints[i-2].x + ppoints[i-2].dxir);
3359       dy2  = ppoints[i-1].y + ppoints[i-1].dyir - (ppoints[i-2].y + ppoints[i-2].dyir);
3360       dx3  = ppoints[i].x +   ppoints[i].dxpr   - (ppoints[i-1].x + ppoints[i-1].dxir);
3361       dy3  = ppoints[i].y +   ppoints[i].dypr   - (ppoints[i-1].y + ppoints[i-1].dyir);
3362 
3363 #ifdef DUMPDEBUGPATH
3364       if ( psfile != NULL )
3365 	fprintf( psfile, "%f %f %f %f %f %f t1srrcurveto %% pindex = %ld\n",
3366 		 dx1*up, dy1*up,
3367 		 dx2*up, dy2*up,
3368 		 dx3*up, dy3*up,
3369 		 i);
3370 #endif
3371       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
3372 	       dx1, dy1, dx2, dy2);
3373       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
3374       B = Loc(CharSpace, dx1, dy1);
3375       C = Loc(CharSpace, dx2, dy2);
3376       D = Loc(CharSpace, dx3, dy3);
3377 
3378       C = Join(C, (struct segment *)Dup(B));
3379       D = Join(D, (struct segment *)Dup(C));
3380       path = Join(path, (struct segment *)Bezier(B, C, D));
3381 
3382       /* 3. Check and handle starting node */
3383       linkNode( i, PATH_END, PATH_RIGHT);
3384 
3385       break;
3386 
3387 
3388     case PPOINT_CLOSEPATH:
3389 #ifdef DEBUG_OUTLINE_SURROUNDING
3390       fprintf( stderr, "RP:  ClosePath command ignored\n");
3391 #endif
3392 
3393       break;
3394 
3395     default:
3396       break;
3397 
3398     }
3399     ++i;
3400   }
3401 
3402   /********************************************************************************
3403    ********************************************************************************
3404    *****
3405    ***** Close right path
3406    *****
3407    ********************************************************************************
3408    ********************************************************************************/
3409 
3410   if ( subpathclosed != 0 ) {
3411     /* We are stroking an outline font to be filled */
3412     if ( closepathatfirst == 0 ) {
3413       /* Because of the concavity issue, we may not simply use
3414 	 the closepath operator here. Instead we have to manage a possible
3415 	 prolongation manually if the closepath would cause a line segment. */
3416 
3417       /* 1. Check and handle starting node */
3418       linkNode( lastind, PATH_START, PATH_RIGHT);
3419 
3420       /* 2. Draw ideal isolated line segment */
3421       dx1  = ppoints[startind].x + ppoints[startind].dxpr - (ppoints[lastind].x + ppoints[lastind].dxnr);
3422       dy1  = ppoints[startind].y + ppoints[startind].dypr - (ppoints[lastind].y + ppoints[lastind].dynr);
3423 #ifdef DUMPDEBUGPATH
3424       if ( psfile != NULL )
3425 	fprintf( psfile, "%f %f t1srlineto %% pindex = %ld\n", dx1*up, dy1*up, startind);
3426 #endif
3427       B = Loc(CharSpace, dx1, dy1);
3428       path = Join(path, Line(B));
3429 
3430       /* 3. Check and handle ending node */
3431       linkNode( startind, PATH_END, PATH_RIGHT);
3432 
3433     } /* if ( closepathatfirst == 0) */
3434 
3435     /* Now close path formally. Anyhow, this won't cause a line segment! */
3436 #ifdef DUMPDEBUGPATH
3437     if ( psfile != NULL ) {
3438       fprintf( psfile, "t1sclosepath %% Right Path finished, stepping to Left Path\n");
3439     }
3440 #endif
3441     tmpseg = Phantom(path);
3442     path = ClosePath(path);
3443     path = Join(Snap(path), tmpseg);
3444 
3445 
3446     /********************************************************************************
3447      ********************************************************************************
3448      *****
3449      ***** Stepping to beginning of left path
3450      *****
3451      ********************************************************************************
3452      ********************************************************************************/
3453 
3454     /* If curve is concave at the subpath's starting point, the location is onCurve
3455        and the left path is convex, there. Conversely, if the curve is convex, the
3456        location is at the right intersection point and the left path will be concave
3457        so that the initial location must be onCurve. Hence, for both cases, we have
3458        to translate back once the intersection shift.
3459 
3460        If the curve is straight at the starting point, we directly jump from the right
3461        intersection point ot he left intersection point.
3462     */
3463     if ( (ppoints[startind].shape == CURVE_CONCAVE) ||
3464 	 (ppoints[startind].shape == CURVE_CONVEX) ) {
3465       dx1 = - ppoints[startind].dxir;
3466       dy1 = - ppoints[startind].dyir;
3467     }
3468     else {
3469       dx1 = - 2.0 * ppoints[startind].dxir;
3470       dy1 = - 2.0 * ppoints[startind].dyir;
3471     }
3472 
3473 #ifdef DUMPDEBUGPATH
3474     if ( psfile != NULL )
3475       fprintf( psfile, "%f %f t1srmoveto %% pindex = %ld\n", dx1*up, dy1*up, startind);
3476 #endif
3477     B = Loc(CharSpace, dx1, dy1);
3478     path = Join(path, B);
3479   } /* if ( subpathclose != 0 */
3480   else {
3481     /* We have a stroked font. In this case, a line segment has to be drawn */
3482     if ( (ppoints[stopind].shape == CURVE_CONCAVE) ||
3483 	 (ppoints[stopind].shape == CURVE_CONVEX) ) {
3484       dx1 = - ppoints[stopind].dxir;
3485       dy1 = - ppoints[stopind].dyir;
3486     }
3487     else {
3488       dx1 = - 2.0 * ppoints[stopind].dxir;
3489       dy1 = - 2.0 * ppoints[stopind].dyir;
3490     }
3491 
3492 #ifdef DUMPDEBUGPATH
3493     if ( psfile != NULL )
3494       fprintf( psfile, "%f %f t1srlineto %% pindex = %ld\n", dx1*up, dy1*up, stopind);
3495 #endif
3496     B = Loc(CharSpace, dx1, dy1);
3497     path = Join(path, Line(B));
3498 
3499   }
3500 
3501 
3502   /********************************************************************************
3503    ********************************************************************************
3504    *****
3505    ***** Construction of left path
3506    *****
3507    ********************************************************************************
3508    ********************************************************************************/
3509 
3510   /* Create left path. This is somewhat more complicated, because the
3511      order/direction has to be exchanged. */
3512 #ifdef DEBUG_OUTLINE_SURROUNDING
3513   fprintf( stderr, "Constructing LeftPath: stopind=%ld, lastind=%ld, closepathatfirst=%d\n",
3514 	   stopind, lastind, closepathatfirst);
3515 #endif
3516   for ( i=stopind; i>=startind; ) {
3517     if ( subpathclosed != 0 ) {
3518       /* closed subpath --> filled font */
3519       if ( i == stopind ) {
3520 	ip   = startind;
3521 	if ( (closepathatfirst != 0) )
3522 	  type = ppoints[ip].type;
3523 	else
3524 	  type = PPOINT_NONE;
3525       }
3526       else if ( i == startind ) {
3527 	ip   = startind + 1;
3528 	type = ppoints[ip].type;
3529       }
3530       else {
3531 	ip   = i + 1;
3532 	type = ppoints[ip].type;
3533       }
3534     }
3535     else {
3536       /* open subpath --> stroked font */
3537       type   = ppoints[i].type;
3538       in     = i - 1;
3539     }
3540 
3541     /* Step through path in inverted direction.
3542        Note: - ip is the index of the starting point, i the index of the
3543                ending point of the current segment.
3544              - If the path point is flagged "concave", then this reverts into
3545                "convex" in the left path and vice versa!
3546 	     - there is an index shift of 1 between closed and open subpaths.
3547     */
3548     switch ( type ) {
3549     case PPOINT_MOVE:
3550 
3551       break;
3552 
3553     case PPOINT_LINE:
3554 
3555       /* handle a line segment */
3556       if ( subpathclosed != 0 ) {
3557 	segendind    = i;
3558 	segstartind  = ip;
3559       }
3560       else {
3561 	segstartind  = i;
3562 	segendind    = in;
3563       }
3564 
3565       /* 1. Check and handle starting node */
3566       linkNode( segstartind, PATH_START, PATH_LEFT);
3567 
3568       /* 2. Draw ideal isolated line segment */
3569 #ifdef DEBUG_OUTLINE_SURROUNDING
3570       fprintf( stderr, "LP:  Line from point %ld to %ld\n", segstartind, segendind);
3571 #endif
3572       dx1  = ppoints[segendind].x - ppoints[segendind].dxnr -
3573 	(ppoints[segstartind].x - ppoints[segstartind].dxpr);
3574       dy1  = ppoints[segendind].y - ppoints[segendind].dynr -
3575 	(ppoints[segstartind].y - ppoints[segstartind].dypr);
3576 #ifdef DUMPDEBUGPATH
3577       if ( psfile != NULL )
3578 	fprintf( psfile, "%f %f t1srlineto %% pindex = %ld\n", dx1*up, dy1*up, segendind);
3579 #endif
3580       B = Loc(CharSpace, dx1, dy1);
3581       path = Join(path, Line(B));
3582 
3583       /* 3. Check and handle ending node */
3584       linkNode( segendind, PATH_END, PATH_LEFT);
3585 
3586       break;
3587 
3588 
3589     case PPOINT_BEZIER_B:
3590       break;
3591 
3592     case PPOINT_BEZIER_C:
3593       break;
3594 
3595     case PPOINT_BEZIER_D:
3596       /* handle a bezier segment (given by this and the previous 3 path points)!
3597 	 For bezier segments, we may not simply apply the intersection of previous
3598 	 and next candidate because that would damage the curve's layout. Instead,
3599 	 in cases where the candidate produced by intersection is not identical to
3600 	 the ideal point, we prolongate and link the distance with a line segment.
3601       */
3602 
3603       /* 1. Check and handle starting node */
3604       linkNode( ip, PATH_START, PATH_LEFT);
3605 
3606       /* 2. Draw ideal curve segment */
3607 #ifdef DEBUG_OUTLINE_SURROUNDING
3608       fprintf( stderr, "LP:  Curve from PP %ld to PP %ld to PP %ld to PP %ld\n",
3609 	       ip, ip-1, ip-2, ip-3);
3610 #endif
3611       /* Use ideal point locations for curve at starting and ending point: */
3612       dx1  = ppoints[ip-1].x - ppoints[ip-1].dxir - (ppoints[ip].x   - ppoints[ip].dxpr);
3613       dy1  = ppoints[ip-1].y - ppoints[ip-1].dyir - (ppoints[ip].y   - ppoints[ip].dypr);
3614       dx2  = ppoints[ip-2].x - ppoints[ip-2].dxir - (ppoints[ip-1].x - ppoints[ip-1].dxir);
3615       dy2  = ppoints[ip-2].y - ppoints[ip-2].dyir - (ppoints[ip-1].y - ppoints[ip-1].dyir);
3616       dx3  = ppoints[ip-3].x - ppoints[ip-3].dxnr - (ppoints[ip-2].x - ppoints[ip-2].dxir);
3617       dy3  = ppoints[ip-3].y - ppoints[ip-3].dynr - (ppoints[ip-2].y - ppoints[ip-2].dyir);
3618 
3619 #ifdef DUMPDEBUGPATH
3620       if ( psfile != NULL )
3621 	fprintf( psfile, "%f %f %f %f %f %f t1srrcurveto %% pindex = %ld\n",
3622 		 dx1*up, dy1*up,
3623 		 dx2*up, dy2*up,
3624 		 dx3*up, dy3*up,
3625 		 i);
3626 #endif
3627       IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ",
3628 	       dx1, dy1, dx2, dy2);
3629       IfTrace2((FontDebug), "%f %f\n", dx3, dy3);
3630       B = Loc(CharSpace, dx1, dy1);
3631       C = Loc(CharSpace, dx2, dy2);
3632       D = Loc(CharSpace, dx3, dy3);
3633 
3634       C = Join(C, (struct segment *)Dup(B));
3635       D = Join(D, (struct segment *)Dup(C));
3636       path = Join(path, (struct segment *)Bezier(B, C, D));
3637 
3638       /* 3. Check and handle ending node */
3639       linkNode( ip-3, PATH_END, PATH_LEFT);
3640 
3641       break;
3642 
3643 
3644     case PPOINT_CLOSEPATH:
3645 
3646       /* Handle a ClosePath segment, if it had
3647 	 caused a line segment. Hence, actually, we handle
3648 	 a line segment here. */
3649       if ( closepathatfirst == 1 ) {
3650 	/* ignore this command */
3651 	break;
3652       }
3653 
3654       /* 1. Check and handle starting node */
3655       linkNode( startind, PATH_START, PATH_LEFT);
3656 
3657       /* 2. Draw ideal isolated line segment */
3658 #ifdef DEBUG_OUTLINE_SURROUNDING
3659       fprintf( stderr, "LP:  Inverted ClosePath from point %ld to %ld\n", startind, lastind);
3660 #endif
3661       if ( subpathclosed != 0 ) {
3662 	dx1  = ppoints[lastind].x - ppoints[lastind].dxnr - (ppoints[startind].x - ppoints[startind].dxpr);
3663 	dy1  = ppoints[lastind].y - ppoints[lastind].dynr - (ppoints[startind].y - ppoints[startind].dypr);
3664       }
3665       else {
3666 	dx1  = -(ppoints[i].x - ppoints[i].dxnr - (ppoints[ip].x - ppoints[ip].dxpr));
3667 	dy1  = -(ppoints[i].y - ppoints[i].dynr - (ppoints[ip].y - ppoints[ip].dypr));
3668       }
3669 
3670 #ifdef DUMPDEBUGPATH
3671       if ( psfile != NULL ) {
3672 	if ( subpathclosed != 0 ) {
3673 	  fprintf( psfile, "%f %f t1srlineto %% (Inverted ClosePath, subpathclosed=1) pindex = %ld\n",
3674 		   dx1*up, dy1*up, lastind);
3675 	}
3676 	else {
3677 	  fprintf( psfile, "%f %f t1srlineto %% (Inverted ClosePath, subpathclosed=0) pindex = %ld\n",
3678 		   dx1*up, dy1*up, i);
3679 	}
3680       }
3681 #endif
3682       B = Loc(CharSpace, dx1, dy1);
3683       path = Join(path, Line(B));
3684 
3685       /* 3. Check and handle ending node */
3686       linkNode( lastind, PATH_END, PATH_LEFT);
3687 
3688       break;
3689 
3690     default:
3691       break;
3692 
3693     }
3694     --i;
3695   }
3696 
3697 #ifdef DUMPDEBUGPATH
3698   if ( psfile != NULL ) {
3699     fprintf( psfile, "t1sclosepath\n");
3700   }
3701 #endif
3702   tmpseg = Phantom(path);
3703   path = ClosePath(path);
3704   path = Join(Snap(path), tmpseg);
3705 
3706 
3707   /********************************************************************************
3708    ********************************************************************************
3709    *****
3710    ***** Move to final position
3711    *****
3712    ********************************************************************************
3713    ********************************************************************************/
3714 
3715   /* Step to back to starting point of this subpath. If closepathatfirst==0, the
3716      final closepath caused a line segment. In this case, we first have to step
3717      back that segment and proceed from this point. */
3718   if ( ppoints[startind].shape == CURVE_CONVEX ) {
3719     /* In this case, left path is concave and the current location is at
3720        the onCurve point */
3721     dx1  = 0.0;
3722     dy1  = 0.0;
3723   }
3724   else {
3725     /* OK, it seems to be the intersection point */
3726     dx1  = ppoints[startind].dxir;
3727     dy1  = ppoints[startind].dyir;
3728   }
3729   /* We are now onCurve. If necessary step to the point where the closepath
3730      appeared. */
3731   if ( closepathatfirst == 0 ) {
3732     dx1 += ppoints[lastind].x - ppoints[startind].x;
3733     dy1 += ppoints[lastind].y - ppoints[startind].y;
3734   }
3735 
3736 
3737 #ifdef DUMPDEBUGPATH
3738   if ( psfile != NULL )
3739     fprintf( psfile, "%f %f t1srmoveto %% pindex = %ld\n", dx1*up, dy1*up, lastind);
3740 #endif
3741   B = Loc(CharSpace, dx1, dy1);
3742   path = Join(path, B);
3743 
3744   return;
3745 
3746 }
3747 
3748 
3749 
3750 /* Compute distance from OnCurve-points to their neighbouring points, fill in
3751    the respective entries dist2prev and dist2next in the ppoints[] structures
3752    and return the index of the last point in the current subpath which has
3753    a location different from the starting point of the subpath. */
computeDistances(long startind,long stopind,int subpathclosed)3754 static long computeDistances( long startind, long stopind, int subpathclosed)
3755 {
3756   long   lastind       = 0;
3757   double dx            = 0.0;
3758   double dy            = 0.0;
3759   long   i             = 0;
3760   int    neighboured   = 0;
3761 
3762 
3763   /* Handle first point as a special case */
3764   /* distance to previous point. First, get index of previous point. */
3765   lastind = stopind;
3766 
3767   if ( subpathclosed != 0 ) {
3768     if ( (ppoints[startind].x == ppoints[stopind].x) &&
3769 	 (ppoints[startind].y == ppoints[stopind].y) ) {
3770       while ( (ppoints[lastind].x == ppoints[stopind].x) &&
3771 	      (ppoints[lastind].y == ppoints[stopind].y))
3772 	--lastind;
3773     }
3774     else {
3775       lastind = stopind - 1;
3776     }
3777   }
3778 
3779 #ifdef DEBUG_OUTLINE_SURROUNDING
3780   fprintf( stderr,
3781 	   "computeDistance(): startind=%ld stopind=%ld, lastind=%ld, start.x=%f, start.y=%f, last.x=%f, last.y=%f\n",
3782 	   startind, stopind, lastind, ppoints[startind].x, ppoints[startind].y,
3783 	   ppoints[lastind].x, ppoints[lastind].y);
3784 #endif
3785 
3786   dx = ppoints[startind].x - ppoints[lastind].x;
3787   dy = ppoints[startind].y - ppoints[lastind].y;
3788   ppoints[startind].dist2prev = sqrt( dx*dx + dy*dy );
3789 
3790   /* distance to next point */
3791   dx = ppoints[startind+1].x - ppoints[startind].x;
3792   dy = ppoints[startind+1].y - ppoints[startind].y;
3793   ppoints[startind].dist2next = sqrt( dx*dx + dy*dy );
3794 
3795 #ifdef DEBUG_OUTLINE_SURROUNDING
3796   fprintf( stderr,
3797 	   "Pre: Distance at point %ld: Prev=%f Next=%f\n",
3798 	   startind, ppoints[startind].dist2prev, ppoints[startind].dist2next);
3799 #endif
3800 
3801   for ( i = startind+1; i < lastind; i++ ) {
3802     if ( (ppoints[i].type == PPOINT_MOVE) ||
3803 	 (ppoints[i].type == PPOINT_LINE) ||
3804 	 (ppoints[i].type == PPOINT_BEZIER_D) ) {
3805       if ( neighboured ) {
3806 	ppoints[i].dist2prev = ppoints[i-1].dist2next;
3807       }
3808       else {
3809 	/* distance to previous point */
3810 	dx = ppoints[i].x - ppoints[i-1].x;
3811 	dy = ppoints[i].y - ppoints[i-1].y;
3812 	/* Take care of degenerated curves */
3813 	if ( (dx == 0.0) && (dy == 0.0) ) {
3814 	  dx = ppoints[i].x - ppoints[i-2].x;
3815 	  dy = ppoints[i].y - ppoints[i-2].y;
3816 	  if ( (dx == 0.0) && (dy == 0.0) ) {
3817 	    dx = ppoints[i].x - ppoints[i-3].x;
3818 	    dy = ppoints[i].y - ppoints[i-3].y;
3819 	  }
3820 	}
3821 	ppoints[i].dist2prev = sqrt( dx*dx + dy*dy );
3822       }
3823       /* distance to next point */
3824       dx = ppoints[i+1].x - ppoints[i].x;
3825       dy = ppoints[i+1].y - ppoints[i].y;
3826       /* Take care of degenerated curves */
3827       if ( (dx == 0.0) && (dy == 0.0) ) {
3828 	dx = ppoints[i+2].x - ppoints[i].x;
3829 	dy = ppoints[i+2].y - ppoints[i].y;
3830 	if ( (dx == 0.0) && (dy == 0.0) ) {
3831 	  dx = ppoints[i+3].x - ppoints[i].x;
3832 	  dy = ppoints[i+3].y - ppoints[i].y;
3833 	}
3834       }
3835       ppoints[i].dist2next = sqrt( dx*dx + dy*dy );
3836       neighboured = 1;
3837 #ifdef DEBUG_OUTLINE_SURROUNDING
3838       fprintf( stderr, "     Distance at point %ld: Prev=%f Next=%f\n",
3839 	       i, ppoints[i].dist2prev, ppoints[i].dist2next);
3840 #endif
3841     }
3842     else {
3843       neighboured = 0;
3844     }
3845 
3846   }
3847   /* We still have to handle the last point */
3848   /* distance to previous point */
3849   dx = ppoints[lastind].x - ppoints[lastind-1].x;
3850   dy = ppoints[lastind].y - ppoints[lastind-1].y;
3851   /* Take care of degenerated curves */
3852   if ( (dx == 0.0) && (dy == 0.0) ) {
3853     dx = ppoints[lastind].x - ppoints[lastind-2].x;
3854     dy = ppoints[lastind].y - ppoints[lastind-2].y;
3855     if ( (dx == 0.0) && (dy == 0.0) ) {
3856       dx = ppoints[lastind].x - ppoints[lastind-3].x;
3857       dy = ppoints[lastind].y - ppoints[lastind-3].y;
3858     }
3859   }
3860   ppoints[lastind].dist2prev = sqrt( dx*dx + dy*dy );
3861   /* distance to next point */
3862   ppoints[lastind].dist2next = ppoints[startind].dist2prev;
3863 #ifdef DEBUG_OUTLINE_SURROUNDING
3864   fprintf( stderr, "End: Distance at point %ld: Prev=%f Next=%f\n",
3865 	   lastind, ppoints[lastind].dist2prev, ppoints[lastind].dist2next);
3866 #endif
3867 
3868   return lastind;
3869 
3870 }
3871 
3872 
3873 
3874 /*
3875 
3876  */
handleNonSubPathSegments(long pindex)3877 static long handleNonSubPathSegments( long pindex)
3878 {
3879 
3880   /* handle the different segment types in a switch-statement */
3881   switch ( ppoints[pindex].type ) {
3882 
3883   case PPOINT_SBW:
3884 #ifdef DUMPDEBUGPATH
3885   if ( psfile != NULL )
3886     fprintf( psfile, "%f %f %f %f t1sbw %% pindex = %ld\n",
3887 	     ppoints[pindex].x*up, ppoints[pindex].y*up,   /* sidebearings */
3888 	     ppoints[pindex].ax*up, ppoints[pindex].ay*up,  /* escapements  */
3889 	     pindex
3890 	     );
3891 #endif
3892     path = Join(path, Loc(CharSpace, ppoints[pindex].x, ppoints[pindex].y));
3893     return 1;
3894     break;
3895 
3896 
3897   case PPOINT_ENDCHAR:
3898     /* Perform a Closepath just in case the command was left out */
3899     path = ClosePath(path);
3900 
3901     /* Set character width / escapement. It is stored in the vars for
3902        hinted coordinates. */
3903     path = Join(Snap(path), Loc(CharSpace, ppoints[pindex].ax, ppoints[pindex].ay));
3904 
3905 #ifdef DUMPDEBUGPATH
3906     if ( psfile != NULL )
3907       fputs( "t1FinishPage\n", psfile);
3908 #endif
3909     return 1;
3910     break;
3911 
3912 
3913   case PPOINT_SEAC:
3914     /* return to origin of accent */
3915     apath = Snap(path);
3916     /* reset path to NULL */
3917     path  = NULL;
3918     return 1;
3919     break;
3920 
3921 
3922   default:
3923     /* not handled, return 0! */
3924     ;
3925   }
3926 
3927   return 0;
3928 
3929 }
3930 
3931 
3932 
3933 /* Transform a path point according to the path's incoming angle, the path's
3934    outgoing angle and the parameter strokewidth. The computation is based on
3935    simple geometric considerations and makes use of the distance from the
3936    current point to the previous point and the next point respectively.
3937 
3938    Generally, each link to a path point induces its own candidate by simply
3939    widening the respective link orthogonally to strokewidth/2. This yields
3940    two displacement vectors (dx,dy) for the link from the previous point to the
3941    point under consideration (dxp, dyp) and and for the link from the current
3942    point to the next point (dxn, dyn).
3943 
3944    Later on, the two candidates are used to compute the resulting displacement
3945    as the intersection of the prolongated lines from before and behind the
3946    current point.
3947 
3948    Additionally, check whether the curve is concave or convex at this point.
3949    This is required for prolongation in the context of stroking.
3950 */
transformOnCurvePathPoint(double strokewidth,long prevind,long currind,long nextind)3951 static void transformOnCurvePathPoint( double strokewidth,
3952 				       long prevind, long currind, long nextind)
3953 {
3954   double distxp;
3955   double distyp;
3956   double distxn;
3957   double distyn;
3958   double det;
3959 
3960   /*
3961   distxp =  (ppoints[currind].y - ppoints[prevind].y);
3962   distyp = -(ppoints[currind].x - ppoints[prevind].x);
3963   distxn =  (ppoints[nextind].y - ppoints[currind].y);
3964   distyn = -(ppoints[nextind].x - ppoints[currind].x);
3965 
3966   ppoints[currind].dxpr = distxp * strokewidth * 0.5 / ppoints[currind].dist2prev;
3967   ppoints[currind].dypr = distyp * strokewidth * 0.5 / ppoints[currind].dist2prev;
3968 
3969   ppoints[currind].dxnr = distxn * strokewidth * 0.5 / ppoints[currind].dist2next;
3970   ppoints[currind].dynr = distyn * strokewidth * 0.5 / ppoints[currind].dist2next;
3971   */
3972   /* Note: When computing transformations of OnCurve points, we consider two
3973            special cases:
3974 
3975 	   1) The OnCurve beginning or end point is equal to the neighboring
3976 	      control point of a Bezier-Segment.
3977 
3978 	   2) This holds for beginning *and* end point. In this case the curve
3979 	      degenerates to a straight lines.
3980 
3981 	   Although this is deprecated by Adobe, fonts that use such constructions
3982 	   exist (e.g.m lower case 'n' of Univers 55). However, we do not care
3983 	   for segments that do not any escapement at all!
3984   */
3985 
3986   distxp =  (ppoints[currind].y - ppoints[prevind].y);
3987   distyp = -(ppoints[currind].x - ppoints[prevind].x);
3988   if ( (distxp == 0.0) && (distyp == 0.0) ) {
3989     distxp =  (ppoints[currind].y - ppoints[prevind-1].y);
3990     distyp = -(ppoints[currind].x - ppoints[prevind-1].x);
3991     if ( (distxp == 0.0) && (distyp == 0.0) ) {
3992       distxp =  (ppoints[currind].y - ppoints[prevind-2].y);
3993       distyp = -(ppoints[currind].x - ppoints[prevind-2].x);
3994     }
3995   }
3996   ppoints[currind].dxpr = distxp * strokewidth * 0.5 / ppoints[currind].dist2prev;
3997   ppoints[currind].dypr = distyp * strokewidth * 0.5 / ppoints[currind].dist2prev;
3998 
3999   distxn =  (ppoints[nextind].y - ppoints[currind].y);
4000   distyn = -(ppoints[nextind].x - ppoints[currind].x);
4001   if ( (distxn == 0.0) && (distyn == 0.0) ) {
4002     distxn =  (ppoints[nextind+1].y - ppoints[currind].y);
4003     distyn = -(ppoints[nextind+1].x - ppoints[currind].x);
4004     if ( (distxn == 0.0) && (distyn == 0.0) ) {
4005       distxn =  (ppoints[nextind+2].y - ppoints[currind].y);
4006       distyn = -(ppoints[nextind+2].x - ppoints[currind].x);
4007     }
4008   }
4009   ppoints[currind].dxnr = distxn * strokewidth * 0.5 / ppoints[currind].dist2next;
4010   ppoints[currind].dynr = distyn * strokewidth * 0.5 / ppoints[currind].dist2next;
4011 
4012   /* Consider determinant of the two tangent vectors at this node in order to
4013      decide whether the curve is convex or cancave at this point. */
4014   if ( (det = ((distxp * distyn) - (distxn * distyp))) < 0.0 ) {
4015     /* curve turns to the right */
4016     ppoints[currind].shape = CURVE_CONCAVE;
4017   }
4018   else if ( det > 0.0 ) {
4019     /* curve turns to the left */
4020     ppoints[currind].shape = CURVE_CONVEX;
4021   }
4022   else {
4023     /* curve is straight */
4024     ppoints[currind].shape = CURVE_STRAIGHT;
4025   }
4026 
4027   return;
4028 }
4029 
4030 
4031 /* Compute a displacement for offCurve points, that is, for Bezier B and C points.
4032 
4033    This computation is not as simple as it might appear at a first glance and,
4034    depending on the actual curve parameters and the parameter strokewidth, it might
4035    be necessary to subdivide the curve. My mathematical background is not actually
4036    reliable in this context but it seems that in particular the angle that the curve
4037    runs through is important in this context. Since the Adobe Type 1 recommendations
4038    on font design include a rule which states that curves' end points should be located
4039    at extreme values, and consequently, that the angle of a curve segment should not
4040    be larger than 90 degrees, I have decided not to implement curve subdivision. This
4041    might lead to some deviations if fonts do not adhere to the Adobe recommendations.
4042    Anyways, I have never seen such fonts.
4043 
4044    This function is called for Bezier_B points and computes displacements for the B
4045    and C points at once. Fortunately, index cycling cannot happen here. When
4046    computing the B' C' off-curve points, special care against numerical instability
4047    is required. We assume that at least one of the two points can be computed
4048    in a stable way.
4049 
4050    The new Bezier B' and C' points can be considered as four degrees of freedom and we have
4051    to find 4 equations to be able to compute them.
4052 
4053    1) We require the tangents slope at point A' to be identical to the slope at the
4054       point A of the ideally thin mathematical curve.
4055 
4056    2) The same holds for the tangents slope at point D' with respect to point D.
4057 
4058    3) We compute the following points
4059 
4060       P1:       Equally subdivides the line A - B
4061       P2:       Equally subdivides the line B - C
4062       P3:       Equally subdivides the line C - D
4063 
4064       P4:       Equally subdivides the line P1 - P2
4065       P5:       Equally subdivides the line P1 - P3
4066 
4067       P6:       Equally subdivides the line P4 - P5
4068 
4069       This latter point is part of the curve and, moreover, the line P4 - P5 is
4070       tangent to the curve at that point.
4071       From this point, we compute a displacement for P6, orthogonally to the curve
4072       at that point and with length strokewidth/2. The resulting point is part of
4073       the delimiting path that makes up the thick curve.
4074 
4075    4) We require that the tangent's slope at P6' equals the tangents slope at P6.
4076 
4077    Then, under certain further constraints as mentioned above, we compute the points
4078    B' and C' making use of the points A' and D' which have been transformed as onCurve
4079    points. By definition, for offCurve points, there is only one candidate.
4080 
4081  */
transformOffCurvePathPoint(double strokewidth,long currind)4082 static void transformOffCurvePathPoint( double strokewidth, long currind)
4083 {
4084   double dtmp;
4085   double diameter;
4086   double dx;
4087   double dy;
4088 
4089   /* points defining the curve */
4090   double pax;
4091   double pay;
4092   double pbx;
4093   double pby;
4094   double pcx;
4095   double pcy;
4096   double pdx;
4097   double pdy;
4098 
4099   /* auxiliary points from iterative Bezier construction */
4100   double p1x;
4101   double p1y;
4102   double p2x;
4103   double p2y;
4104   double p3x;
4105   double p3y;
4106   double p4x;
4107   double p4y;
4108   double p5x;
4109   double p5y;
4110   double p6x;
4111   double p6y;
4112 
4113   /* already displaced / shifted onCurve points and the ones we are going
4114      to compute. */
4115   double paxs;
4116   double pays;
4117   double pbxs;
4118   double pbys;
4119   double pcxs;
4120   double pcys;
4121   double pdxs;
4122   double pdys;
4123 
4124   /* The normal vector on the curve at t=1/2 */
4125   double nabs;
4126   double nx;
4127   double ny;
4128 
4129   /* some variables for computations at point B' */
4130   double bloc1x;
4131   double bloc1y;
4132   double bdir1x;
4133   double bdir1y;
4134   double bloc2x;
4135   double bloc2y;
4136   double bdir2x;
4137   double bdir2y;
4138   double bdet;
4139   double binvdet;
4140   double binvdir1x;
4141   double binvdir1y; /**/
4142   double binvdir2x;
4143   double binvdir2y; /**/
4144   double bmu;
4145   double bnu; /**/
4146 
4147   /* some variables for computations at point C' */
4148   double cloc1x;
4149   double cloc1y;
4150   double cdir1x;
4151   double cdir1y;
4152   double cloc2x;
4153   double cloc2y;
4154   double cdir2x;
4155   double cdir2y;
4156   double cdet;
4157   double cinvdet;
4158   double cinvdir1x;
4159   double cinvdir1y; /**/
4160   double cinvdir2x;
4161   double cinvdir2y; /**/
4162   double cmu;
4163   double cnu; /**/
4164 
4165   diameter = strokewidth * 0.5;
4166 
4167   pax = ppoints[currind-1].x;
4168   pay = ppoints[currind-1].y;
4169   pbx = ppoints[currind].x;
4170   pby = ppoints[currind].y;
4171   pcx = ppoints[currind+1].x;
4172   pcy = ppoints[currind+1].y;
4173   pdx = ppoints[currind+2].x;
4174   pdy = ppoints[currind+2].y;
4175 
4176   p1x = (pax + pbx) * 0.5;
4177   p1y = (pay + pby) * 0.5;
4178   p2x = (pbx + pcx) * 0.5;
4179   p2y = (pby + pcy) * 0.5;
4180   p3x = (pcx + pdx) * 0.5;
4181   p3y = (pcy + pdy) * 0.5;
4182   p4x = (p1x + p2x) * 0.5;
4183   p4y = (p1y + p2y) * 0.5;
4184   p5x = (p2x + p3x) * 0.5;
4185   p5y = (p2y + p3y) * 0.5;
4186   p6x = (p4x + p5x) * 0.5;
4187   p6y = (p4y + p5y) * 0.5;
4188 
4189 
4190   /* We start by computing the shift of the onCurve points. It is not possible
4191      to use  dxr / dyr of the ppoints-stucture entries. These values have been
4192      computed by intersection of both links to a path point. Here we need the
4193      ideal points of the thick isolated curve segment. We are aware that for
4194      Bezier splines, control point and OnCurve point might be identical! */
4195   dx   =   (ppoints[currind].y - ppoints[currind-1].y) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
4196   dy   = - (ppoints[currind].x - ppoints[currind-1].x) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
4197   if ( (dx == 0.0) && (dy == 0.0) ) {
4198     /* Bezier_A and Bezier_B are identical */
4199     dx   =   (ppoints[currind+1].y - ppoints[currind-1].y) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
4200     dy   = - (ppoints[currind+1].x - ppoints[currind-1].x) * strokewidth * 0.5 / ppoints[currind-1].dist2next;
4201   }
4202   paxs = ppoints[currind-1].x + dx;
4203   pays = ppoints[currind-1].y + dy;
4204   dx   =   (ppoints[currind+2].y - ppoints[currind+1].y) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
4205   dy   = - (ppoints[currind+2].x - ppoints[currind+1].x) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
4206   if ( (dx == 0.0) && (dy == 0.0) ) {
4207     /* Bezier_C and Bezier_D are identical */
4208     dx   =   (ppoints[currind+2].y - ppoints[currind].y) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
4209     dy   = - (ppoints[currind+2].x - ppoints[currind].x) * strokewidth * 0.5 / ppoints[currind+2].dist2prev;
4210   }
4211   pdxs = ppoints[currind+2].x + dx;
4212   pdys = ppoints[currind+2].y + dy;
4213 
4214   /* Next, we compute the right side normal vector at the curve point t=1/2,
4215    that is, at P6. */
4216   nabs    = diameter / sqrt(((p5x - p4x) * (p5x - p4x)) + ((p5y - p4y) * (p5y - p4y)));
4217   nx      = (p5y - p4y) * nabs;
4218   ny      = (p4x - p5x) * nabs;
4219 
4220 #ifdef DEBUG_OUTLINE_SURROUNDING
4221   fprintf( stderr, "transformOffCurvePathPoint():\n");
4222   fprintf( stderr, "    A=(%f,%f), B=(%f,%f), C=(%f,%f), D=(%f,%f)\n",
4223 	   pax, pay, pbx, pby, pcx, pcy, pdx, pdy);
4224   fprintf( stderr, "    PathInfo: Curve from PP %ld ... PP %ld ... PP %ld ... PP %ld. StrokeWidth=%f.\n",
4225 	   currind-1, currind, currind+1, currind+2, strokewidth);
4226   /*
4227   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",
4228 	   pax, pay, pbx, pby, pcx, pcy, pdx, pdy);
4229   */
4230   fprintf( stderr, "    As=(%f,%f), Ds=(%f,%f)\n",
4231 	   paxs, pays, pdxs, pdys);
4232   fprintf( stderr, "    p6=(%f,%f)\n", p6x, p6y);
4233   fprintf( stderr, "    nx=%f, ny=%f, nabs=%f\n", nx, ny, nabs);
4234   fprintf( stderr, "    p6s=(%f,%f)\n", p6x+nx, p6y+ny);
4235 #endif
4236 
4237   /* Compute two lines whose intersection will define point B' */
4238   bloc1x = (4.0 * (nx + p6x) - (2 * paxs) + pdxs) / 3.0;
4239   bloc1y = (4.0 * (ny + p6y) - (2 * pays) + pdys) / 3.0;
4240   bdir1x = pcx + pdx - pax - pbx;
4241   bdir1y = pcy + pdy - pay - pby;
4242   bloc2x = paxs;
4243   bloc2y = pays;
4244   bdir2x = pbx - pax;
4245   bdir2y = pby - pay;
4246   bdet   = (bdir2x * bdir1y) - (bdir2y * bdir1x);
4247 
4248 #define DET_QUOTIENT_UPPER_THRESHOLD      (1.05)
4249 #define DET_QUOTIENT_LOWER_THRESHOLD      (1.0/DET_QUOTIENT_UPPER_THRESHOLD)
4250 
4251   /* Life has shown, that the "reliability" of the determinant has to be
4252      ensured. Otherwise, serious distortions might be introduced.
4253      In order to ensure numerical stability, we do not only check whether
4254      the detrminant is about zero, but we also check whether the two partial
4255      expressions that are subtracted when computing the determinant are of
4256      about the same size. If this is the case, we explicitly reset the
4257      determinant and eventually compute this off-curve point based on the
4258      other off-curve point later. */
4259   if ( (bdir2x != 0.0) && (bdir1y != 0.0) ) {
4260     dtmp = (bdir2y*bdir1x)/(bdir2x*bdir1y);
4261     if ( (DET_QUOTIENT_LOWER_THRESHOLD < dtmp) &&
4262 	 (DET_QUOTIENT_UPPER_THRESHOLD > dtmp)
4263 	 ) {
4264       /* Determinant appears to be unreliable, reset it exactly to zero. */
4265       bdet = 0.0;
4266 #ifdef DEBUG_OUTLINE_SURROUNDING
4267       fprintf( stderr, "    Warning: Determinant quotient check for bdet failed: dtmp=%16.16f, lower limit=%f, upper limit=%f.\n    --> Determinant does not seem to be stable, resetting to zero.\n",
4268 	       dtmp, DET_QUOTIENT_LOWER_THRESHOLD, DET_QUOTIENT_UPPER_THRESHOLD);
4269 #endif
4270     }
4271   }
4272   else if ( (bdir2y != 0.0) && (bdir1x != 0.0) ) {
4273     dtmp = (bdir2x*bdir1y)/(bdir2y*bdir1x);
4274     if ( (DET_QUOTIENT_LOWER_THRESHOLD < dtmp) &&
4275 	 (DET_QUOTIENT_UPPER_THRESHOLD > dtmp)
4276 	 ) {
4277       /* Determinant appears to be unreliable, reset it exactly to zero. */
4278       bdet = 0.0;
4279 #ifdef DEBUG_OUTLINE_SURROUNDING
4280       fprintf( stderr, "    Warning: Determinant quotient check for bdet failed: dtmp=%16.16f, lower limit=%f, upper limit=%f.\n    --> Determinant does not seem to be stable, resetting to zero.\n",
4281 	       dtmp, DET_QUOTIENT_LOWER_THRESHOLD, DET_QUOTIENT_UPPER_THRESHOLD);
4282 #endif
4283     }
4284   }
4285 
4286 
4287 #ifdef DEBUG_OUTLINE_SURROUNDING
4288   fprintf( stderr, "    bloc1x=%f, bloc1y=%f, bloc2x,=%f bloc2y=%f\n",
4289 	   bloc1x, bloc1y, bloc2x, bloc2y);
4290   fprintf( stderr, "    bdir1x=%f, bdir1y=%f, bdir2x,=%f bdir2y=%f\n",
4291 	   bdir1x, bdir1y, bdir2x, bdir2y);
4292 #endif
4293 
4294   /* Switch if determinant is zero; we then actually have a straight line */
4295   if ( fabs(bdet) < 0.001 ) {
4296     pbxs   = pbx + nx;
4297     pbys   = pby + ny;
4298     bmu    = 0.0;
4299     bnu    = 0.0;
4300 #ifdef DEBUG_OUTLINE_SURROUNDING
4301     fprintf( stderr, "    Warning: Determinant check for bdet failed: bdet=%16.16f. Computing Bs based on normal vector, resetting bmu, bnu.\n",
4302 	     bdet);
4303 #endif
4304   }
4305   else {
4306     /* Calculate required part of inverse matrix */
4307     binvdet   =   1.0 / bdet;
4308     binvdir2x =   bdir1y * binvdet;
4309     binvdir2y = - bdir2y * binvdet; /**/
4310     binvdir1x = - bdir1x * binvdet;
4311     binvdir1y =   bdir2x * binvdet; /**/
4312 
4313     /* Calculate coefficient that describes intersection */
4314     bmu       =   (binvdir2x * (bloc1x - bloc2x)) + (binvdir1x * (bloc1y - bloc2y));
4315     bnu       =   (binvdir2y * (bloc1x - bloc2x)) + (binvdir1y * (bloc1y - bloc2y)); /**/
4316 
4317     /* Calculate B' */
4318     pbxs      =   bloc2x + (bmu * bdir2x);
4319     pbys      =   bloc2y + (bmu * bdir2y);
4320   }
4321 
4322   /* Compute two lines whose intersection will define point C' */
4323   cloc1x = (4.0 * (nx + p6x) - (2 * pdxs) + paxs) / 3.0;
4324   cloc1y = (4.0 * (ny + p6y) - (2 * pdys) + pays) / 3.0;
4325   cdir1x = bdir1x;
4326   cdir1y = bdir1y;
4327   cloc2x = pdxs;
4328   cloc2y = pdys;
4329   cdir2x = pcx - pdx;
4330   cdir2y = pcy - pdy;
4331   cdet   = (cdir2x * cdir1y) - (cdir2y * cdir1x);
4332 
4333   /* Life has shown, that the "reliability" of the determinant has to be
4334      ensured. Otherwise, serious distortions might be introduced.
4335      In order to ensure numerical stability, we do not only check whether
4336      the detrminant is about zero, but we also check whether the two partial
4337      expressions that are subtracted when computing the determinant are of
4338      about the same size. If this is the case, we explicitly reset the
4339      determinant and eventually compute this off-curve point based on the
4340      other off-curve point later. */
4341   if ( (cdir2x != 0.0) && (cdir1y != 0.0) ) {
4342     dtmp = (cdir2y*cdir1x)/(cdir2x*cdir1y);
4343     if ( (DET_QUOTIENT_LOWER_THRESHOLD < dtmp) &&
4344 	 (DET_QUOTIENT_UPPER_THRESHOLD > dtmp)
4345 	 ) {
4346       /* Determinant appears to be unreliable, reset it exactly to zero. */
4347       cdet = 0.0;
4348 #ifdef DEBUG_OUTLINE_SURROUNDING
4349       fprintf( stderr, "    Warning: Determinant quotient check for cdet failed: dtmp=%16.16f, lower limit=%f, upper limit=%f.\n    --> Determinant does not seem to be stable, resetting to zero.\n",
4350 	       dtmp, DET_QUOTIENT_LOWER_THRESHOLD, DET_QUOTIENT_UPPER_THRESHOLD);
4351 #endif
4352     }
4353   }
4354   else if ( (cdir2y != 0.0) && (cdir1x != 0.0) ) {
4355     dtmp = (cdir2x*cdir1y)/(cdir2y*cdir1x);
4356     if ( (DET_QUOTIENT_LOWER_THRESHOLD < dtmp) &&
4357 	 (DET_QUOTIENT_UPPER_THRESHOLD > dtmp)
4358 	 ) {
4359       /* Determinant appears to be unreliable, reset it exactly to zero. */
4360       cdet = 0.0;
4361 #ifdef DEBUG_OUTLINE_SURROUNDING
4362       fprintf( stderr, "    Warning: Determinant quotient check for cdet failed: dtmp=%16.16f, lower limit=%f, upper limit=%f.\n    --> Determinant does not seem to be stable, resetting to zero.\n",
4363 	       dtmp, DET_QUOTIENT_LOWER_THRESHOLD, DET_QUOTIENT_UPPER_THRESHOLD);
4364 #endif
4365     }
4366   }
4367 
4368 #ifdef DEBUG_OUTLINE_SURROUNDING
4369   fprintf( stderr, "    cloc1x=%f, cloc1y=%f, cloc2x,=%f cloc2y=%f\n",
4370 	   cloc1x, cloc1y, cloc2x, cloc2y);
4371   fprintf( stderr, "    cdir1x=%f, cdir1y=%f, cdir2x,=%f cdir2y=%f\n",
4372 	   cdir1x, cdir1y, cdir2x, cdir2y);
4373 #endif
4374 
4375   /* Switch if determinant is zero; we then actually have a straight line */
4376   if ( fabs( cdet) < 0.001 ) {
4377     pcxs   = pcx + nx;
4378     pcys   = pcy + ny;
4379     cmu    = 0.0;
4380     cnu    = 0.0;
4381 #ifdef DEBUG_OUTLINE_SURROUNDING
4382     fprintf( stderr, "    Warning: Determinant check for cdet failed: cdet=%16.16f. Computing Cs based on normal vector, resetting cmu, cnu.\n",
4383 	     cdet);
4384 #endif
4385   }
4386   else {
4387     /* Calculate required part of inverse matrix */
4388     cinvdet   =   1.0 / cdet;
4389     cinvdir2x =   cdir1y * cinvdet;
4390     cinvdir2y = - cdir2y * cinvdet; /**/
4391     cinvdir1x = - cdir1x * cinvdet;
4392     cinvdir1y =   cdir2x * cinvdet; /**/
4393 
4394     /* Calculate coefficient that describes intersection */
4395     cmu       =   (cinvdir2x * (cloc1x - cloc2x)) + (cinvdir1x * (cloc1y - cloc2y));
4396     cnu       =   (cinvdir2y * (cloc1x - cloc2x)) + (cinvdir1y * (cloc1y - cloc2y)); /**/
4397 
4398     /* Calculate C' */
4399     pcxs      =   cloc2x + (cmu * cdir2x);
4400     pcys      =   cloc2y + (cmu * cdir2y);
4401   }
4402 
4403 #ifdef DEBUG_OUTLINE_SURROUNDING
4404   fprintf( stderr, "    bdet=%f, cdet=%f, bmu=%f, bnu=%f, cmu=%f, cnu=%f\n",
4405 	   bdet, cdet, bmu, bnu, cmu, cnu);
4406 #endif
4407 
4408   /* Analyse coefficients and decide on numerical stability. If suggesting,
4409      overwrite, using another relation. Here, we assume that at least the
4410      solution at *one* end of the curve is stable. */
4411   if ( fabs(bmu) < 0.1 ) {
4412     pbxs = ((8 * (nx + p6x) - paxs - pdxs) / 3.0) - pcxs;
4413     pbys = ((8 * (ny + p6y) - pays - pdys) / 3.0) - pcys;
4414 #ifdef DEBUG_OUTLINE_SURROUNDING
4415     fprintf( stderr, "    Warning: Coefficient check for bmu failed: bmu=%16.16f. Computing Bs based on Cs.\n",
4416 	     bmu);
4417 #endif
4418   }
4419   if ( fabs(cmu) < 0.1 ) {
4420     pcxs = ((8 * (nx + p6x) - paxs - pdxs) / 3.0) - pbxs;
4421     pcys = ((8 * (ny + p6y) - pays - pdys) / 3.0) - pbys;
4422 #ifdef DEBUG_OUTLINE_SURROUNDING
4423     fprintf( stderr, "    Warning: Coefficient check for cmu failed: cmu=%16.16f. Computing Cs based on Bs.\n",
4424 	     cmu);
4425 #endif
4426   }
4427 
4428 
4429   /* Store the resulting displacement values in the ppoints-struct so
4430      they can be used for path construction. We use the "intersect" member
4431      because in this case nothing is related to "previous" or "next".*/
4432 #ifdef DEBUG_OUTLINE_SURROUNDING
4433   fprintf( stderr, "    pbx=%f, pbxs=%f, bxshift=%f, pby=%f, pbys=%f, byshift=%f\n",
4434 	   pbx, pbxs, pbxs-pbx, pby, pbys, pbys-pby);
4435   fprintf( stderr, "    pcx=%f, pcxs=%f, cxshift=%f, pcy=%f, pcys=%f, cyshift=%f\n",
4436 	   pcx, pcxs, pcxs-pcx, pcy, pcys, pcys-pcy);
4437   fprintf( stderr, "    Summary:    A =(%f,%f), B =(%f,%f), C =(%f,%f), D =(%f,%f)\n",
4438 	   pax, pay, pbx, pby, pcx, pcy, pdx, pdy);
4439   fprintf( stderr, "                As=(%f,%f), Bs=(%f,%f), Cs=(%f,%f), Ds=(%f,%f)\n\n",
4440 	   paxs, pays, pbxs, pbys, pcxs, pcys, pdxs, pdys);
4441 #endif
4442   ppoints[currind].dxir    = pbxs - pbx;
4443   ppoints[currind].dyir    = pbys - pby;
4444   ppoints[currind+1].dxir  = pcxs - pcx;
4445   ppoints[currind+1].dyir  = pcys - pcy;
4446 
4447   return;
4448 
4449 }
4450 
4451 
intersectRight(long index,double halfwidth,long flag)4452 static void intersectRight( long index, double halfwidth, long flag)
4453 {
4454   double r2  = 0.0;
4455   double det = 0.0;
4456   double dxprev;
4457   double dyprev;
4458   double dxnext;
4459   double dynext;
4460 
4461 
4462   /* In order to determine the intersection between the two
4463      prolongations at the path point under consideration, we use
4464      the Hesse Normal Form, multiplied with r.
4465 
4466      dx * x + dy * y + r^2 = 0
4467 
4468      Here, r is the distance from the origin, that is, from the path point
4469      under consideration. */
4470 
4471   /* Check for start and ending of non-closed paths */
4472   if ( flag == INTERSECT_PREVIOUS ) {
4473     ppoints[index].dxir = ppoints[index].dxpr;
4474     ppoints[index].dyir = ppoints[index].dypr;
4475     /* Correct shape to be "straight" at ending point! */
4476     ppoints[index].shape = CURVE_STRAIGHT;
4477     return;
4478   }
4479   if ( flag == INTERSECT_NEXT ) {
4480     ppoints[index].dxir = ppoints[index].dxnr;
4481     ppoints[index].dyir = ppoints[index].dynr;
4482     /* Correct shape to be "straight" at starting point! */
4483     ppoints[index].shape = CURVE_STRAIGHT;
4484     return;
4485   }
4486 
4487   /* OK, we actually compute an intersection */
4488   dxprev = ppoints[index].dxpr;
4489   dyprev = ppoints[index].dypr;
4490   dxnext = ppoints[index].dxnr;
4491   dynext = ppoints[index].dynr;
4492 
4493   /* Compute distance square */
4494   r2 = halfwidth * halfwidth;
4495 
4496   /* Check the determinant. If it is zero, the two lines are parallel
4497      and also must touch at atleast one location,
4498      so that there are an infinite number of solutions. In this case,
4499      we compute the average position and are done. */
4500   if ( fabs( (det = ((dyprev * dxnext) - (dynext * dxprev))) ) < 0.00001 ) {
4501     ppoints[index].dxir = 0.5 * (dxprev + dxnext);
4502     ppoints[index].dyir = 0.5 * (dyprev + dynext);
4503 #ifdef DEBUG_OUTLINE_SURROUNDING
4504     fprintf( stderr, "intersectRight(0):\n    dxprev=%f, dxnext=%f, dxres=%f,\n    dyprev=%f, dynext=%f, dyres=%f,\n    det=%16.16f\n",
4505 	     dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir, det);
4506     fprintf( stderr, "    --> Computation based on averaging [dxprev,dyprev] and [dxnext,dynext]\n");
4507     fprintf( stderr, "    Right intersection point shift: (%f,%f), absolute shift length: %f.\n\n",
4508 	     ppoints[index].dxir, ppoints[index].dyir,
4509 	     sqrt(ppoints[index].dxir*ppoints[index].dxir + ppoints[index].dyir*ppoints[index].dyir));
4510 #endif
4511     return;
4512   }
4513   /* OK, there seems to be a unique solution, compute it */
4514   if ( dxprev != 0.0 ) {
4515     ppoints[index].dyir =  r2 * (dxnext - dxprev) / det;
4516     ppoints[index].dxir = (r2 - (dyprev * ppoints[index].dyir)) / dxprev; /* - ? */
4517 #ifdef DEBUG_OUTLINE_SURROUNDING
4518     fprintf( stderr, "intersectRight(1):\n    dxprev=%f, dxnext=%f, dxres=%f,\n    dyprev=%f, dynext=%f, dyres=%f,\n    det=%16.16f\n",
4519 	     dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir, det);
4520     fprintf( stderr, "    --> Computation based on previous path point.\n");
4521     fprintf( stderr, "    Right intersection point shift: (%f,%f), absolute shift length: %f.\n\n",
4522 	     ppoints[index].dxir, ppoints[index].dyir,
4523 	     sqrt(ppoints[index].dxir*ppoints[index].dxir + ppoints[index].dyir*ppoints[index].dyir));
4524 #endif
4525   }
4526   else {
4527     ppoints[index].dyir = -r2 * (dxprev - dxnext) / det;
4528     ppoints[index].dxir = (r2 - (dynext * ppoints[index].dyir)) / dxnext; /* - ? */
4529 #ifdef DEBUG_OUTLINE_SURROUNDING
4530     fprintf( stderr, "intersectRight(2):\n    dxprev=%f, dxnext=%f, dxres=%f,\n    dyprev=%f, dynext=%f, dyres=%f,\n    det=%16.16f\n",
4531 	     dxprev, dxnext, ppoints[index].dxir, dyprev, dynext, ppoints[index].dyir, det);
4532     fprintf( stderr, "    --> Computation based on next path point.\n");
4533     fprintf( stderr, "    Right intersection point shift: (%f,%f), absolute shift length: %f.\n\n",
4534 	     ppoints[index].dxir, ppoints[index].dyir,
4535 	     sqrt(ppoints[index].dxir*ppoints[index].dxir + ppoints[index].dyir*ppoints[index].dyir));
4536 #endif
4537   }
4538 
4539   return;
4540 
4541 }
4542 
4543 
4544 
4545 /* linkNode(): Insert prolongation lines at nodes. */
linkNode(long index,int position,int orientation)4546 static void linkNode( long index, int position, int orientation)
4547 {
4548   struct segment* B;
4549   double dx = 0.0;
4550   double dy = 0.0;
4551 
4552   if ( orientation == PATH_RIGHT ) {
4553     /* We are constructing the right hand side path */
4554     if ( position == PATH_START ) {
4555       /* We are starting a new segment. Link from current point to ideally
4556 	 next-shifted point of segment. */
4557       if ( ppoints[index].shape == CURVE_CONCAVE ) {
4558 	/* prolongate from original curve point to ideally next-shifted point */
4559 	dx = ppoints[index].dxnr;
4560 	dy = ppoints[index].dynr;
4561 #ifdef DEBUG_OUTLINE_SURROUNDING
4562 	fprintf( stderr, "RP:  Concave at PP %ld. Prolongation from onCurve to ideal: (%f,%f)\n",
4563 		 index, dx, dy);
4564 #endif
4565       }
4566       else if ( ppoints[index].shape == CURVE_CONVEX ) {
4567 	/* prolongate from intersecion point to ideally next-shifted point */
4568 	dx = ppoints[index].dxnr - ppoints[index].dxir;
4569 	dy = ppoints[index].dynr - ppoints[index].dyir;
4570 #ifdef DEBUG_OUTLINE_SURROUNDING
4571 	fprintf( stderr, "RP:  Convex at PP %ld. Prolongation from intersection to ideal: (%f,%f)\n",
4572 		 index, dx, dy);
4573 #endif
4574       }
4575     }
4576     else if ( position == PATH_END ) {
4577       /* We are ending the current segment. Link from ideally prev-shifted point
4578 	 to the appropriate ending point. */
4579       if ( ppoints[index].shape == CURVE_CONCAVE ) {
4580 	/* prolongate from ideally prev-shifted point to original curve point. */
4581 	dx = - ppoints[index].dxpr;
4582 	dy = - ppoints[index].dypr;
4583 #ifdef DEBUG_OUTLINE_SURROUNDING
4584 	fprintf( stderr, "RP:  Concave at PP %ld. Prolongation from ideal to onCurve: (%f,%f)\n",
4585 		 index, dx, dy);
4586 #endif
4587       }
4588       else if ( ppoints[index].shape == CURVE_CONVEX ) {
4589 	/* prolongate from ideally prev-shifted point to intersection point. */
4590 	dx = ppoints[index].dxir - ppoints[index].dxpr;
4591 	dy = ppoints[index].dyir - ppoints[index].dypr;
4592 #ifdef DEBUG_OUTLINE_SURROUNDING
4593 	fprintf( stderr, "RP:  Convex at PP %ld. Prolongation from ideal to intersection: (%f,%f)\n",
4594 		 index, dx, dy);
4595 #endif
4596       }
4597     } /* if ( PATH_END ) */
4598   } /* if ( PATH_RIGHT ) */
4599   else if ( orientation == PATH_LEFT ) {
4600 
4601     /* We are constructing the left hand side path. Some notions have to be
4602        reverted (e.g. concavity vs. convexity and next vs. previous)! */
4603     if ( position == PATH_START ) {
4604       /* We are starting a new segment. Link from current point to ideally
4605 	 next-shifted point of segment. */
4606       if ( ppoints[index].shape == CURVE_CONVEX ) {
4607 	/* prolongate from original curve point to ideally next-shifted point.
4608 	   Remember: next --> prev! */
4609 	dx = - (ppoints[index].dxpr);
4610 	dy = - (ppoints[index].dypr);
4611 #ifdef DEBUG_OUTLINE_SURROUNDING
4612 	fprintf( stderr, "LP:  Concave at PP %ld. Prolongation from onCurve to ideal: (%f,%f)\n",
4613 		 index, dx, dy);
4614 #endif
4615       }
4616       else if ( ppoints[index].shape == CURVE_CONCAVE ) {
4617 	/* prolongate from intersecion point to ideally next-shifted point */
4618 	dx = - (ppoints[index].dxpr - ppoints[index].dxir);
4619 	dy = - (ppoints[index].dypr - ppoints[index].dyir);
4620 #ifdef DEBUG_OUTLINE_SURROUNDING
4621 	fprintf( stderr, "LP:  Convex at PP %ld. Prolongation from intersection to ideal: (%f,%f)\n",
4622 		 index, dx, dy);
4623 #endif
4624       }
4625     }/* if ( PATH_START ) */
4626     else if ( position == PATH_END ) {
4627       /* We are ending the current segment. Link from ideally prev-shifted point
4628 	 to the appropriate ending point. */
4629       if ( ppoints[index].shape == CURVE_CONVEX ) {
4630 	/* prolongate from ideally prev-shifted point to original curve point. */
4631 	dx = ppoints[index].dxnr;
4632 	dy = ppoints[index].dynr;
4633 #ifdef DEBUG_OUTLINE_SURROUNDING
4634 	fprintf( stderr, "LP:  Concave at PP %ld. Prolongation from ideal to onCurve: (%f,%f)\n",
4635 		 index, dx, dy);
4636 #endif
4637       }
4638       else if ( ppoints[index].shape == CURVE_CONCAVE ) {
4639 	/* prolongate from ideally prev-shifted point to intersection point. */
4640 	dx = - (ppoints[index].dxir - ppoints[index].dxnr);
4641 	dy = - (ppoints[index].dyir - ppoints[index].dynr);
4642 #ifdef DEBUG_OUTLINE_SURROUNDING
4643 	fprintf( stderr, "LP:  Convex at PP %ld. Prolongation from ideal to intersection: (%f,%f)\n",
4644 		 index, dx, dy);
4645 #endif
4646       }
4647     } /* if ( PATH_END ) */
4648   } /* if ( PATH_LEFT ) */
4649 
4650   if ( (dx != 0.0) || (dy != 0.0) ) {
4651 #ifdef DUMPDEBUGPATH
4652     if ( psfile != NULL )
4653       fprintf( psfile, "%f %f t1sprolongate %% pindex = %ld\n", dx*up, dy*up, index);
4654 #endif
4655     B = Loc( CharSpace, dx, dy);
4656     path = Join(path, Line(B));
4657   }
4658 
4659   return;
4660 
4661 }
4662 
4663 
T1int_Type1QuerySEAC(unsigned char * base,unsigned char * accent)4664 int T1int_Type1QuerySEAC( unsigned char* base,
4665 			  unsigned char* accent)
4666 {
4667   if ( isseac == 0 ) {
4668     return 0;
4669   }
4670 
4671   *base   = seacbase;
4672   *accent = seacaccent;
4673 
4674   return isseac;
4675 }
4676 
4677