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