1 /* $XConsortium: type1.c,v 1.7 94/02/07 15:30:22 gildea Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
4 * Copyright Lexmark International, Inc. 1991
6 * Portions Copyright (c) 1990 Adobe Systems Incorporated.
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.
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.
32 /*********************************************************************/
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. */
38 /* Authors: Sten F. Andler, IBM Almaden Research Center */
39 /* (Type 1 interpreter, stem & flex hints) */
41 /* Patrick A. Casey, Lexmark International, Inc. */
42 /* (Font level hints & stem hints) */
44 /*********************************************************************/
49 #include <stdio.h> /* a system-dependent include, usually */
54 #include "fonts.h" /* understands about TEXTTYPEs */
55 #include "pictures.h" /* understands about handles */
57 typedef struct xobject xobject;
58 #include "util.h" /* PostScript objects */
59 #include "blues.h" /* Blues structure for font-level hints */
61 /**********************************/
62 /* Type1 Constants and Structures */
63 /**********************************/
64 #define MAXSTACK 24 /* Adobe Type1 limit */
65 #define MAXCALLSTACK 10 /* Adobe Type1 limit */
66 #define MAXPSFAKESTACK 32 /* Max depth of fake PostScript stack (local) */
67 #define MAXSTRLEN 512 /* Max length of a Type 1 string (local) */
68 #define MAXLABEL 256 /* Maximum number of new hints */
69 #define MAXSTEMS 128 /* Maximum number of VSTEM and HSTEM hints */
70 #define EPS 0.001 /* Small number for comparisons */
72 /************************************/
73 /* Adobe Type 1 CharString commands */
74 /************************************/
93 /*******************************************/
94 /* Adobe Type 1 CharString Escape commands */
95 /*******************************************/
102 #define CALLOTHERSUBR 16
104 #define SETCURRENTPOINT 33
109 static double tmpx; /* Store macro argument in tmpx to avoid re-evaluation */
110 static long tmpi; /* Store converted value in tmpi to avoid re-evaluation */
112 #define FABS(x) (((tmpx = (x)) < 0.0) ? -tmpx : tmpx)
114 #define CEIL(x) (((tmpi = (long) (tmpx = (x))) < tmpx) ? ++tmpi : tmpi)
116 #define FLOOR(x) (((tmpi = (long) (tmpx = (x))) > tmpx) ? --tmpi : tmpi)
118 #define ROUND(x) FLOOR((x) + 0.5)
120 #define ODD(x) (((int)(x)) & 01)
122 #define Error {errflag = TRUE; return;}
123 #define ErrorRet(ret) {errflag = TRUE; return (ret);}
125 #define Error0(errmsg) {IfTrace0(TRUE, errmsg); Error;}
126 #define Error0Ret(errmsg, ret) {IfTrace0(TRUE, errmsg); ErrorRet(ret);}
128 #define Error1(errmsg,arg) {IfTrace1(TRUE, errmsg, arg); Error;}
130 /********************/
131 /* global variables */
132 /********************/
133 struct stem { /* representation of a STEM hint */
134 int vertical; /* TRUE if vertical, FALSE otherwise */
135 double x, dx; /* interval of vertical stem */
136 double y, dy; /* interval of horizontal stem */
137 struct segment *lbhint, *lbrevhint; /* left or bottom hint adjustment */
138 struct segment *rthint, *rtrevhint; /* right or top hint adjustment */
141 extern struct XYspace *IDENTITY;
143 static double escapementX, escapementY;
144 static double sidebearingX, sidebearingY;
145 static double accentoffsetX, accentoffsetY;
147 static struct segment *path;
150 /*************************************************/
151 /* Global variables to hold Type1Char parameters */
152 /*************************************************/
153 static char *Environment;
154 static struct XYspace *CharSpace;
155 static psobj *CharStringP, *SubrsP, *OtherSubrsP;
158 /************************/
159 /* Forward declarations */
160 /************************/
162 static double PSFakePop();
169 static DoClosePath();
177 static CallOtherSubr();
178 static SetCurrentPoint();
180 /*****************************************/
181 /* statics for Flex procedures (FlxProc) */
182 /*****************************************/
183 static struct segment *FlxOldPath; /* save path before Flex feature */
185 /******************************************************/
186 /* statics for Font level hints (Blues) (see blues.h) */
187 /******************************************************/
188 static struct blues_struct *blues; /* the blues structure */
189 static struct alignmentzone alignmentzones[MAXALIGNMENTZONES];
190 static int numalignmentzones; /* total number of alignment zones */
192 /****************************************************************/
193 /* Subroutines for the Font level hints (Alignment zones, etc.) */
194 /****************************************************************/
196 /******************************************/
197 /* Fill in the alignment zone structures. */
198 /******************************************/
199 static ComputeAlignmentZones()
202 double dummy, bluezonepixels, familyzonepixels;
205 numalignmentzones = 0; /* initialize total # of zones */
207 /* do the BlueValues zones */
208 for (i = 0; i < blues->numBlueValues; i +=2, ++numalignmentzones) {
209 /* the 0th & 1st numbers in BlueValues are for a bottom zone */
210 /* the rest are topzones */
211 if (i == 0) /* bottom zone */
212 alignmentzones[numalignmentzones].topzone = FALSE;
214 alignmentzones[numalignmentzones].topzone = TRUE;
215 if (i < blues->numFamilyBlues) { /* we must consider FamilyBlues */
216 p = ILoc(CharSpace,0,blues->BlueValues[i] - blues->BlueValues[i+1]);
217 QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
219 p = ILoc(CharSpace,0,blues->FamilyBlues[i]-blues->FamilyBlues[i+1]);
220 QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
222 /* is the difference in size of the zones less than 1 pixel? */
223 if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
224 /* use the Family zones */
225 alignmentzones[numalignmentzones].bottomy =
226 blues->FamilyBlues[i];
227 alignmentzones[numalignmentzones].topy =
228 blues->FamilyBlues[i+1];
232 /* use this font's Blue zones */
233 alignmentzones[numalignmentzones].bottomy = blues->BlueValues[i];
234 alignmentzones[numalignmentzones].topy = blues->BlueValues[i+1];
237 /* do the OtherBlues zones */
238 for (i = 0; i < blues->numOtherBlues; i +=2, ++numalignmentzones) {
239 /* all of the OtherBlues zones are bottom zones */
240 alignmentzones[numalignmentzones].topzone = FALSE;
241 if (i < blues->numFamilyOtherBlues) {/* consider FamilyOtherBlues */
242 p = ILoc(CharSpace,0,blues->OtherBlues[i] - blues->OtherBlues[i+1]);
243 QueryLoc(p, IDENTITY, &dummy, &bluezonepixels);
245 p = ILoc(CharSpace,0,blues->FamilyOtherBlues[i] -
246 blues->FamilyOtherBlues[i+1]);
247 QueryLoc(p, IDENTITY, &dummy, &familyzonepixels);
249 /* is the difference in size of the zones less than 1 pixel? */
250 if (FABS(bluezonepixels - familyzonepixels) < 1.0) {
251 /* use the Family zones */
252 alignmentzones[numalignmentzones].bottomy =
253 blues->FamilyOtherBlues[i];
254 alignmentzones[numalignmentzones].topy =
255 blues->FamilyOtherBlues[i+1];
259 /* use this font's Blue zones (as opposed to the Family Blues */
260 alignmentzones[numalignmentzones].bottomy = blues->OtherBlues[i];
261 alignmentzones[numalignmentzones].topy = blues->OtherBlues[i+1];
265 /**********************************************************************/
266 /* Subroutines and statics for handling of the VSTEM and HSTEM hints. */
267 /**********************************************************************/
268 static int InDotSection; /* DotSection flag */
269 static struct stem stems[MAXSTEMS]; /* All STEM hints */
270 static int numstems; /* Number of STEM hints */
271 static int currstartstem; /* The current starting stem. */
272 static int oldvert, oldhor; /* Remember hint in effect */
273 static int oldhorhalf, oldverthalf; /* Remember which half of the stem */
274 static double wsoffsetX, wsoffsetY; /* White space offset - for VSTEM3,HSTEM3 */
275 static int wsset; /* Flag for whether we've set wsoffsetX,Y */
277 static InitStems() /* Initialize the STEM hint data structures */
279 InDotSection = FALSE;
280 currstartstem = numstems = 0;
281 oldvert = oldhor = -1;
284 static FinitStems() /* Terminate the STEM hint data structures */
288 for (i = 0; i < numstems; i++) {
289 Destroy(stems[i].lbhint);
290 Destroy(stems[i].lbrevhint);
291 Destroy(stems[i].rthint);
292 Destroy(stems[i].rtrevhint);
296 /*******************************************************************/
297 /* Compute the dislocation that a stemhint should cause for points */
298 /* inside the stem. */
299 /*******************************************************************/
300 static ComputeStem(stemno)
303 int verticalondevice, idealwidth;
304 double stemstart, stemwidth;
307 double stembottom, stemtop, flatposition;
308 double Xpixels, Ypixels;
309 double unitpixels, onepixel;
310 int suppressovershoot, enforceovershoot;
311 double stemshift, flatpospixels, overshoot;
312 double widthdiff; /* Number of character space units to adjust width */
313 double lbhintvalue, rthintvalue;
314 double cxx, cyx, cxy, cyy; /* Transformation matrix */
315 int rotated; /* TRUE if character is on the side, FALSE if upright */
317 /************************************************/
318 /* DETERMINE ORIENTATION OF CHARACTER ON DEVICE */
319 /************************************************/
321 QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
323 if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
324 rotated = TRUE; /* Char is on side (90 or 270 degrees), possibly oblique. */
325 else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
326 rotated = FALSE; /* Char is upright (0 or 180 degrees), possibly oblique. */
328 stems[stemno].lbhint = NULL; /* Char is at non-axial angle, ignore hints. */
329 stems[stemno].lbrevhint = NULL;
330 stems[stemno].rthint = NULL;
331 stems[stemno].rtrevhint = NULL;
335 /* Determine orientation of stem */
337 if (stems[stemno].vertical) {
338 verticalondevice = !rotated;
339 stemstart = stems[stemno].x;
340 stemwidth = stems[stemno].dx;
342 verticalondevice = rotated;
343 stemstart = stems[stemno].y;
344 stemwidth = stems[stemno].dy;
347 /* Determine how many pixels (non-negative) correspond to 1 character space
348 unit (unitpixels), and how many character space units (non-negative)
349 correspond to one pixel (onepixel). */
351 if (stems[stemno].vertical)
352 p = ILoc(CharSpace, 1, 0);
354 p = ILoc(CharSpace, 0, 1);
355 QueryLoc(p, IDENTITY, &Xpixels, &Ypixels);
357 if (verticalondevice)
358 unitpixels = FABS(Xpixels);
360 unitpixels = FABS(Ypixels);
362 onepixel = 1.0 / unitpixels;
364 /**********************/
365 /* ADJUST STEM WIDTHS */
366 /**********************/
370 /* Find standard stem with smallest width difference from this stem */
371 if (stems[stemno].vertical) { /* vertical stem */
372 if (blues->StdVW != 0) /* there is an entry for StdVW */
373 widthdiff = blues->StdVW - stemwidth;
374 for (i = 0; i < blues->numStemSnapV; ++i) { /* now look at StemSnapV */
375 if (blues->StemSnapV[i] - stemwidth < widthdiff)
376 /* this standard width is the best match so far for this stem */
377 widthdiff = blues->StemSnapV[i] - stemwidth;
379 } else { /* horizontal stem */
380 if (blues->StdHW != 0) /* there is an entry for StdHW */
381 widthdiff = blues->StdHW - stemwidth;
382 for (i = 0; i < blues->numStemSnapH; ++i) { /* now look at StemSnapH */
383 if (blues->StemSnapH[i] - stemwidth < widthdiff)
384 /* this standard width is the best match so far for this stem */
385 widthdiff = blues->StemSnapH[i] - stemwidth;
389 /* Only expand or contract stems if they differ by less than 1 pixel from
390 the closest standard width, otherwise make the width difference = 0. */
391 if (FABS(widthdiff) > onepixel)
394 /* Expand or contract stem to the nearest integral number of pixels. */
395 idealwidth = ROUND((stemwidth + widthdiff) * unitpixels);
396 /* Ensure that all stems are at least one pixel wide. */
399 /* Apply ForceBold to vertical stems. */
400 if (blues->ForceBold && stems[stemno].vertical)
401 /* Force this vertical stem to be at least DEFAULTBOLDSTEMWIDTH wide. */
402 if (idealwidth < DEFAULTBOLDSTEMWIDTH)
403 idealwidth = DEFAULTBOLDSTEMWIDTH;
404 /* Now compute the number of character space units necessary */
405 widthdiff = idealwidth * onepixel - stemwidth;
407 /*********************************************************************/
408 /* ALIGNMENT ZONES AND OVERSHOOT SUPPRESSION - HORIZONTAL STEMS ONLY */
409 /*********************************************************************/
413 if (!stems[stemno].vertical) {
415 /* Get bottom and top boundaries of the stem. */
416 stembottom = stemstart;
417 stemtop = stemstart + stemwidth;
419 /* Find out if this stem intersects an alignment zone (the BlueFuzz */
420 /* entry in the Private dictionary specifies the number of character */
421 /* units to extend (in both directions) the effect of an alignment */
422 /* zone on a horizontal stem. The default value of BlueFuzz is 1. */
423 for (i = 0; i < numalignmentzones; ++i) {
424 if (alignmentzones[i].topzone) {
425 if (stemtop >= alignmentzones[i].bottomy &&
426 stemtop <= alignmentzones[i].topy + blues->BlueFuzz) {
427 break; /* We found a top-zone */
430 if (stembottom <= alignmentzones[i].topy &&
431 stembottom >= alignmentzones[i].bottomy - blues->BlueFuzz) {
432 break; /* We found a bottom-zone */
437 if (i < numalignmentzones) { /* We found an intersecting zone (number i). */
438 suppressovershoot = FALSE;
439 enforceovershoot = FALSE;
441 /* When 1 character space unit is rendered smaller than BlueScale
442 device units (pixels), we must SUPPRESS overshoots. Otherwise,
443 if the top (or bottom) of this stem is more than BlueShift character
444 space units away from the flat position, we must ENFORCE overshoot. */
446 if (unitpixels < blues->BlueScale)
447 suppressovershoot = TRUE;
449 if (alignmentzones[i].topzone)
450 if (stemtop >= alignmentzones[i].bottomy + blues->BlueShift)
451 enforceovershoot = TRUE;
453 if (stembottom <= alignmentzones[i].topy - blues->BlueShift)
454 enforceovershoot = TRUE;
456 /*************************************************/
457 /* ALIGN THE FLAT POSITION OF THE ALIGNMENT ZONE */
458 /*************************************************/
460 /* Compute the position of the alignment zone's flat position in
461 device space and the amount of shift needed to align it on a
462 pixel boundary. Move all stems this amount. */
464 if (alignmentzones[i].topzone)
465 flatposition = alignmentzones[i].bottomy;
467 flatposition = alignmentzones[i].topy;
469 /* Find the flat position in pixels */
470 flatpospixels = flatposition * unitpixels;
472 /* Find the stem shift necessary to align the flat
473 position on a pixel boundary, and use this shift for all stems */
474 stemshift = (ROUND(flatpospixels) - flatpospixels) * onepixel;
476 /************************************************/
477 /* HANDLE OVERSHOOT ENFORCEMENT AND SUPPRESSION */
478 /************************************************/
480 /* Compute overshoot amount (non-negative) */
481 if (alignmentzones[i].topzone)
482 overshoot = stemtop - flatposition;
484 overshoot = flatposition - stembottom;
486 if (overshoot > 0.0) {
487 /* ENFORCE overshoot by shifting the entire stem (if necessary) so that
488 it falls at least one pixel beyond the flat position. */
490 if (enforceovershoot)
491 if (overshoot < onepixel)
492 if (alignmentzones[i].topzone)
493 stemshift += onepixel - overshoot;
495 stemshift -= onepixel - overshoot;
497 /* SUPPRESS overshoot by aligning the stem to the alignment zone's
500 if (suppressovershoot)
501 if (alignmentzones[i].topzone)
502 stemshift -= overshoot;
504 stemshift += overshoot;
507 /************************************************************/
508 /* COMPUTE HINT VALUES FOR EACH SIDE OF THE HORIZONTAL STEM */
509 /************************************************************/
511 /* If the stem was aligned by a topzone, we expand or contract the stem
512 only at the bottom - since the stem top was aligned by the zone.
513 If the stem was aligned by a bottomzone, we expand or contract the stem
514 only at the top - since the stem bottom was aligned by the zone. */
515 if (alignmentzones[i].topzone) {
516 lbhintvalue = stemshift - widthdiff; /* bottom */
517 rthintvalue = stemshift; /* top */
519 lbhintvalue = stemshift; /* bottom */
520 rthintvalue = stemshift + widthdiff; /* top */
523 stems[stemno].lbhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, lbhintvalue));
524 stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -lbhintvalue));
525 stems[stemno].rthint = (struct segment *)Permanent(Loc(CharSpace, 0.0, rthintvalue));
526 stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -rthintvalue));
530 } /* endif (i < numalignmentzones) */
532 /* We didn't find any alignment zones intersecting this stem, so
533 proceed with normal stem alignment below. */
535 } /* endif (!stems[stemno].vertical) */
537 /* Align stem with pixel boundaries on device */
538 stemstart = stemstart - widthdiff / 2;
539 stemshift = ROUND(stemstart * unitpixels) * onepixel - stemstart;
541 /* Adjust the boundaries of the stem */
542 lbhintvalue = stemshift - widthdiff / 2; /* left or bottom */
543 rthintvalue = stemshift + widthdiff / 2; /* right or top */
545 if (stems[stemno].vertical) {
546 stems[stemno].lbhint = (struct segment *)Permanent(Loc(CharSpace, lbhintvalue, 0.0));
547 stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, -lbhintvalue, 0.0));
548 stems[stemno].rthint = (struct segment *)Permanent(Loc(CharSpace, rthintvalue, 0.0));
549 stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, -rthintvalue, 0.0));
551 stems[stemno].lbhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, lbhintvalue));
552 stems[stemno].lbrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -lbhintvalue));
553 stems[stemno].rthint = (struct segment *)Permanent(Loc(CharSpace, 0.0, rthintvalue));
554 stems[stemno].rtrevhint = (struct segment *)Permanent(Loc(CharSpace, 0.0, -rthintvalue));
563 /*********************************************************************/
564 /* Adjust a point using the given stem hint. Use the left/bottom */
565 /* hint value or the right/top hint value depending on where the */
566 /* point lies in the stem. */
567 /*********************************************************************/
568 static struct segment *Applyhint(p, stemnumber, half)
570 int stemnumber, half;
572 if (half == LEFT || half == BOTTOM)
573 return Join(p, stems[stemnumber].lbhint); /* left or bottom hint */
575 return Join(p, stems[stemnumber].rthint); /* right or top hint */
578 /*********************************************************************/
579 /* Adjust a point using the given reverse hint. Use the left/bottom */
580 /* hint value or the right/top hint value depending on where the */
581 /* point lies in the stem. */
582 /*********************************************************************/
583 static struct segment *Applyrevhint(p, stemnumber, half)
585 int stemnumber, half;
587 if (half == LEFT || half == BOTTOM)
588 return Join(p, stems[stemnumber].lbrevhint); /* left or bottom hint */
590 return Join(p, stems[stemnumber].rtrevhint); /* right or top hint */
593 /***********************************************************************/
594 /* Find the vertical and horizontal stems that the current point */
595 /* (x, y) may be involved in. At most one horizontal and one vertical */
596 /* stem can apply to a single point, since there are no overlaps */
598 /* The actual hintvalue is returned as a location. */
599 /* Hints are ignored inside a DotSection. */
600 /***********************************************************************/
601 static struct segment *FindStems(x, y, dx, dy)
607 int newhorhalf, newverthalf;
609 if (InDotSection) return(NULL);
611 newvert = newhor = -1;
612 newhorhalf = newverthalf = -1;
614 for (i = currstartstem; i < numstems; i++) {
615 if (stems[i].vertical) { /* VSTEM hint */
616 if ((x >= stems[i].x - EPS) &&
617 (x <= stems[i].x+stems[i].dx + EPS)) {
620 if (dy < 0) newverthalf = LEFT;
621 else newverthalf = RIGHT;
623 if (x < stems[i].x+stems[i].dx / 2) newverthalf = LEFT;
624 else newverthalf = RIGHT;
627 } else { /* HSTEM hint */
628 if ((y >= stems[i].y - EPS) &&
629 (y <= stems[i].y+stems[i].dy + EPS)) {
632 if (dx < 0) newhorhalf = TOP;
633 else newhorhalf = BOTTOM;
635 if (y < stems[i].y+stems[i].dy / 2) newhorhalf = BOTTOM;
636 else newhorhalf = TOP;
644 if (newvert == -1 && oldvert == -1) ; /* Outside of any hints */
645 else if (newvert == oldvert &&
646 newverthalf == oldverthalf); /* No hint change */
647 else if (oldvert == -1) { /* New vertical hint in effect */
648 p = Applyhint(p, newvert, newverthalf);
649 } else if (newvert == -1) { /* Old vertical hint no longer in effect */
650 p = Applyrevhint(p, oldvert, oldverthalf);
651 } else { /* New vertical hint in effect, old hint no longer in effect */
652 p = Applyrevhint(p, oldvert, oldverthalf);
653 p = Applyhint(p, newvert, newverthalf);
656 if (newhor == -1 && oldhor == -1) ; /* Outside of any hints */
657 else if (newhor == oldhor &&
658 newhorhalf == oldhorhalf) ; /* No hint change */
659 else if (oldhor == -1) { /* New horizontal hint in effect */
660 p = Applyhint(p, newhor, newhorhalf);
661 } else if (newhor == -1) { /* Old horizontal hint no longer in effect */
662 p = Applyrevhint(p, oldhor, oldhorhalf);
664 else { /* New horizontal hint in effect, old hint no longer in effect */
665 p = Applyrevhint(p, oldhor, oldhorhalf);
666 p = Applyhint(p, newhor, newhorhalf);
669 oldvert = newvert; oldverthalf = newverthalf;
670 oldhor = newhor; oldhorhalf = newhorhalf;
675 /******************************************************/
676 /* Subroutines and statics for the Type1Char routines */
677 /******************************************************/
679 static int strindex; /* index into PostScript string being interpreted */
680 static double currx, curry; /* accumulated x and y values for hints */
682 struct callstackentry {
683 psobj *currstrP; /* current CharStringP */
684 int currindex; /* current strindex */
685 unsigned short currkey; /* current decryption key */
688 static double Stack[MAXSTACK];
690 static struct callstackentry CallStack[MAXCALLSTACK];
692 static double PSFakeStack[MAXPSFAKESTACK];
693 static int PSFakeTop;
703 if (++Top < MAXSTACK) Stack[Top] = Num;
704 else Error0("Push: Stack full\n");
707 static ClearCallStack()
712 static PushCall(CurrStrP, CurrIndex, CurrKey)
715 unsigned short CurrKey;
717 if (++CallTop < MAXCALLSTACK) {
718 CallStack[CallTop].currstrP = CurrStrP; /* save CharString pointer */
719 CallStack[CallTop].currindex = CurrIndex; /* save CharString index */
720 CallStack[CallTop].currkey = CurrKey; /* save decryption key */
722 else Error0("PushCall: Stack full\n");
725 static PopCall(CurrStrPP, CurrIndexP, CurrKeyP)
728 unsigned short *CurrKeyP;
731 *CurrStrPP = CallStack[CallTop].currstrP; /* restore CharString pointer */
732 *CurrIndexP = CallStack[CallTop].currindex; /* restore CharString index */
733 *CurrKeyP = CallStack[CallTop--].currkey; /* restore decryption key */
735 else Error0("PopCall: Stack empty\n");
738 static ClearPSFakeStack()
743 /* PSFakePush: Pushes a number onto the fake PostScript stack */
744 static PSFakePush(Num)
747 if (++PSFakeTop < MAXPSFAKESTACK) PSFakeStack[PSFakeTop] = Num;
748 else Error0("PSFakePush: Stack full\n");
751 /* PSFakePop: Removes a number from the top of the fake PostScript stack */
752 static double PSFakePop ()
754 if (PSFakeTop >= 0) return(PSFakeStack[PSFakeTop--]);
755 else Error0Ret("PSFakePop : Stack empty\n", 0.0);
759 /***********************************************************************/
760 /* Center a stem on the pixel grid -- used by HStem3 and VStem3 */
761 /***********************************************************************/
762 static struct segment *CenterStem(edge1, edge2)
766 int idealwidth, verticalondevice;
767 double leftx, lefty, rightx, righty, center, width;
768 double widthx, widthy;
769 double shift, shiftx, shifty;
770 double Xpixels, Ypixels;
773 p = Loc(CharSpace, edge1, 0.0);
774 QueryLoc(p, IDENTITY, &leftx, &lefty);
776 p = Join(p, Loc(CharSpace, edge2, 0.0));
777 QueryLoc(p, IDENTITY, &rightx, &righty);
780 widthx = FABS(rightx - leftx);
781 widthy = FABS(righty - lefty);
783 if (widthy <= EPS) { /* verticalondevice hint */
784 verticalondevice = TRUE;
785 center = (rightx + leftx) / 2.0;
788 else if (widthx <= EPS) { /* horizontal hint */
789 verticalondevice = FALSE;
790 center = (righty + lefty) / 2.0;
793 else { /* neither horizontal nor verticalondevice and not oblique */
797 idealwidth = ROUND(width);
798 if (idealwidth == 0) idealwidth = 1;
799 if (ODD(idealwidth)) { /* is ideal width odd? */
800 /* center stem over pixel */
801 shift = FLOOR(center) + 0.5 - center;
804 /* align stem on pixel boundary */
805 shift = ROUND(center) - center;
808 if (verticalondevice) {
816 p = Loc(IDENTITY, shiftx, shifty);
817 QueryLoc(p, CharSpace, &Xpixels, &Ypixels);
818 wsoffsetX = Xpixels; wsoffsetY = Ypixels;
819 currx += wsoffsetX; curry += wsoffsetY;
824 /*-----------------------------------------------------------------------
825 Decrypt - From Adobe Type 1 book page 63, with some modifications
826 -----------------------------------------------------------------------*/
827 #define KEY 4330 /* Initial key (seed) for CharStrings decryption */
828 #define C1 52845 /* Multiplier for pseudo-random number generator */
829 #define C2 22719 /* Constant for pseudo-random number generator */
831 static unsigned short r; /* Pseudo-random sequence of keys */
833 static unsigned char Decrypt(cipher)
834 unsigned char cipher;
838 plain = cipher ^ (r >> 8);
839 r = (cipher + r) * C1 + C2;
843 /* Get the next byte from the codestring being interpreted */
844 static int DoRead(CodeP)
847 if (strindex >= CharStringP->len) return(FALSE); /* end of string */
848 *CodeP = Decrypt((unsigned char) CharStringP->data.stringP[strindex++]);
852 /* Strip blues->lenIV bytes from CharString and update encryption key */
853 /* (the lenIV entry in the Private dictionary specifies the number of */
854 /* random bytes at the beginning of each CharString; default is 4) */
855 static void StartDecrypt()
859 r = KEY; /* Initial key (seed) for CharStrings decryption */
860 for (strindex = 0; strindex < blues->lenIV;)
861 if (!DoRead(&Code)) /* Read a byte and update decryption key */
862 Error0("StartDecrypt: Premature end of CharString\n");
868 int Code1, Code2, Code3, Code4;
870 if (Code <= 31) /* Code is [0,31] */
872 else if (Code <= 246) /* Code is [32,246] */
873 Push((double)(Code - 139));
874 else if (Code <= 250) { /* Code is [247,250] */
875 if (!DoRead(&Code2)) goto ended;
876 Push((double)(((Code - 247) << 8) + Code2 + 108));
878 else if (Code <= 254) { /* Code is [251,254] */
879 if (!DoRead(&Code2)) goto ended;
880 Push((double)( -((Code - 251) << 8) - Code2 - 108));
882 else { /* Code is 255 */
883 if (!DoRead(&Code1)) goto ended;
884 if (!DoRead(&Code2)) goto ended;
885 if (!DoRead(&Code3)) goto ended;
886 if (!DoRead(&Code4)) goto ended;
887 Push((double)((((((Code1<<8) + Code2)<<8) + Code3)<<8) + Code4));
891 ended: Error0("Decode: Premature end of Type 1 CharString");
894 /* Interpret a command code */
895 static DoCommand(Code)
899 case HSTEM: /* |- y dy HSTEM |- */
900 /* Vertical range of a horizontal stem zone */
901 if (Top < 1) Error0("DoCommand: Stack low\n");
902 HStem(Stack[0], Stack[1]);
905 case VSTEM: /* |- x dx VSTEM |- */
906 /* Horizontal range of a vertical stem zone */
907 if (Top < 1) Error0("DoCommand: Stack low\n");
908 VStem(Stack[0], Stack[1]);
911 case VMOVETO: /* |- dy VMOVETO |- */
912 /* Vertical MOVETO, equivalent to 0 dy RMOVETO */
913 if (Top < 0) Error0("DoCommand: Stack low\n");
914 RMoveTo(0.0, Stack[0]);
917 case RLINETO: /* |- dx dy RLINETO |- */
918 /* Like RLINETO in PostScript */
919 if (Top < 1) Error0("DoCommand: Stack low\n");
920 RLineTo(Stack[0], Stack[1]);
923 case HLINETO: /* |- dx HLINETO |- */
924 /* Horizontal LINETO, equivalent to dx 0 RLINETO */
925 if (Top < 0) Error0("DoCommand: Stack low\n");
926 RLineTo(Stack[0], 0.0);
929 case VLINETO: /* |- dy VLINETO |- */
930 /* Vertical LINETO, equivalent to 0 dy RLINETO */
931 if (Top < 0) Error0("DoCommand: Stack low\n");
932 RLineTo(0.0, Stack[0]);
936 /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
937 /* Relative RCURVETO, equivalent to dx1 dy1 */
938 /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
939 /* (dy1+dy2+dy3) RCURVETO in PostScript */
940 if (Top < 5) Error0("DoCommand: Stack low\n");
941 RRCurveTo(Stack[0], Stack[1], Stack[2], Stack[3],
945 case CLOSEPATH: /* - CLOSEPATH |- */
946 /* Closes a subpath without repositioning the */
951 case CALLSUBR: /* subr# CALLSUBR - */
952 /* Calls a CharString subroutine with index */
953 /* subr# from the Subrs array */
954 if (Top < 0) Error0("DoCommand: Stack low\n");
955 CallSubr((int)Stack[Top--]);
957 case RETURN: /* - RETURN - */
958 /* Returns from a Subrs array CharString */
959 /* subroutine called with CALLSUBR */
962 case ESCAPE: /* ESCAPE to two-byte command code */
963 if (!DoRead(&Code)) Error0("DoCommand: ESCAPE is last byte\n");
966 case HSBW: /* |- sbx wx HSBW |- */
967 /* Set the left sidebearing point to (sbx,0), */
968 /* set the character width vector to (wx,0). */
969 /* Equivalent to sbx 0 wx 0 SBW. Space */
970 /* character should have sbx = 0 */
971 if (Top < 1) Error0("DoCommand: Stack low\n");
972 Sbw(Stack[0], 0.0, Stack[1], 0.0);
975 case ENDCHAR: /* - ENDCHAR |- */
976 /* Finishes a CharString outline */
980 case RMOVETO: /* |- dx dy RMOVETO |- */
981 /* Behaves like RMOVETO in PostScript */
982 if (Top < 1) Error0("DoCommand: Stack low\n");
983 RMoveTo(Stack[0], Stack[1]);
986 case HMOVETO: /* |- dx HMOVETO |- */
987 /* Horizontal MOVETO. Equivalent to dx 0 RMOVETO */
988 if (Top < 0) Error0("DoCommand: Stack low\n");
989 RMoveTo(Stack[0], 0.0);
992 case VHCURVETO: /* |- dy1 dx2 dy2 dx3 VHCURVETO |- */
993 /* Vertical-Horizontal CURVETO, equivalent to */
994 /* 0 dy1 dx2 dy2 dx3 0 RRCURVETO */
995 if (Top < 3) Error0("DoCommand: Stack low\n");
996 RRCurveTo(0.0, Stack[0], Stack[1], Stack[2],
1000 case HVCURVETO: /* |- dx1 dx2 dy2 dy3 HVCURVETO |- */
1001 /* Horizontal-Vertical CURVETO, equivalent to */
1002 /* dx1 0 dx2 dy2 0 dy3 RRCURVETO */
1003 if (Top < 3) Error0("DoCommand: Stack low\n");
1004 RRCurveTo(Stack[0], 0.0, Stack[1], Stack[2], 0.0, Stack[3]);
1007 default: /* Unassigned command code */
1009 Error1("DoCommand: Unassigned code %d\n", Code);
1020 case DOTSECTION: /* - DOTSECTION |- */
1021 /* Brackets an outline section for the dots in */
1022 /* letters such as "i", "j", and "!". */
1026 case VSTEM3: /* |- x0 dx0 x1 dx1 x2 dx2 VSTEM3 |- */
1027 /* Declares the horizontal ranges of three */
1028 /* vertical stem zones between x0 and x0+dx0, */
1029 /* x1 and x1+dx1, and x2 and x2+dx2. */
1030 if (Top < 5) Error0("DoCommand: Stack low\n");
1031 if (!wsset && ProcessHints) {
1032 /* Shift the whole character so that the middle stem is centered. */
1033 p = CenterStem(Stack[2] + sidebearingX, Stack[3]);
1034 path = Join(path, p);
1038 VStem(Stack[0], Stack[1]);
1039 VStem(Stack[2], Stack[3]);
1040 VStem(Stack[4], Stack[5]);
1043 case HSTEM3: /* |- y0 dy0 y1 dy1 y2 dy2 HSTEM3 |- */
1044 /* Declares the vertical ranges of three hori- */
1045 /* zontal stem zones between y0 and y0+dy0, */
1046 /* y1 and y1+dy1, and y2 and y2+dy2. */
1047 if (Top < 5) Error0("DoCommand: Stack low\n");
1048 HStem(Stack[0], Stack[1]);
1049 HStem(Stack[2], Stack[3]);
1050 HStem(Stack[4], Stack[5]);
1053 case SEAC: /* |- asb adx ady bchar achar SEAC |- */
1054 /* Standard Encoding Accented Character. */
1055 if (Top < 4) Error0("DoCommand: Stack low\n");
1056 Seac(Stack[0], Stack[1], Stack[2],
1057 (unsigned char) Stack[3],
1058 (unsigned char) Stack[4]);
1061 case SBW: /* |- sbx sby wx wy SBW |- */
1062 /* Set the left sidebearing point to (sbx,sby), */
1063 /* set the character width vector to (wx,wy). */
1064 if (Top < 3) Error0("DoCommand: Stack low\n");
1065 Sbw(Stack[0], Stack[1], Stack[2], Stack[3]);
1068 case DIV: /* num1 num2 DIV quotient */
1069 /* Behaves like DIV in the PostScript language */
1070 if (Top < 1) Error0("DoCommand: Stack low\n");
1071 Stack[Top-1] = Div(Stack[Top-1], Stack[Top]);
1075 /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
1076 /* Make calls on the PostScript interpreter */
1077 if (Top < 1) Error0("DoCommand: Stack low\n");
1079 if (Top < Num+1) Error0("DoCommand: Stack low\n");
1080 for (i = 0; i < Num; i++) PSFakePush(Stack[Top - i - 2]);
1082 CallOtherSubr((int)Stack[Top + Num + 2]);
1084 case POP: /* - POP number */
1085 /* Removes a number from the top of the */
1086 /* PostScript interpreter stack and pushes it */
1087 /* onto the Type 1 BuildChar operand stack */
1090 case SETCURRENTPOINT: /* |- x y SETCURRENTPOINT |- */
1091 /* Sets the current point to (x,y) in absolute */
1092 /* character space coordinates without per- */
1093 /* forming a CharString MOVETO command */
1094 if (Top < 1) Error0("DoCommand: Stack low\n");
1095 SetCurrentPoint(Stack[0], Stack[1]);
1098 default: /* Unassigned escape code command */
1100 Error1("Escape: Unassigned code %d\n", Code);
1104 /* |- y dy HSTEM |- */
1105 /* Declares the vertical range of a horizontal stem zone */
1106 /* between coordinates y and y + dy */
1107 /* y is relative to the left sidebearing point */
1111 IfTrace2((FontDebug), "Hstem %f %f\n", &y, &dy);
1113 if (numstems >= MAXSTEMS) Error0("HStem: Too many hints\n");
1114 if (dy < 0.0) {y += dy; dy = -dy;}
1115 stems[numstems].vertical = FALSE;
1116 stems[numstems].x = 0.0;
1117 stems[numstems].y = sidebearingY + y + wsoffsetY;
1118 stems[numstems].dx = 0.0;
1119 stems[numstems].dy = dy;
1120 ComputeStem(numstems);
1125 /* |- x dx VSTEM |- */
1126 /* Declares the horizontal range of a vertical stem zone */
1127 /* between coordinates x and x + dx */
1128 /* x is relative to the left sidebearing point */
1132 IfTrace2((FontDebug), "Vstem %f %f\n", &x, &dx);
1134 if (numstems >= MAXSTEMS) Error0("VStem: Too many hints\n");
1135 if (dx < 0.0) {x += dx; dx = -dx;}
1136 stems[numstems].vertical = TRUE;
1137 stems[numstems].x = sidebearingX + x + wsoffsetX;
1138 stems[numstems].y = 0.0;
1139 stems[numstems].dx = dx;
1140 stems[numstems].dy = 0.0;
1141 ComputeStem(numstems);
1146 /* |- dx dy RLINETO |- */
1147 /* Behaves like RLINETO in PostScript */
1148 static RLineTo(dx, dy)
1153 IfTrace2((FontDebug), "RLineTo %f %f\n", &dx, &dy);
1155 B = Loc(CharSpace, dx, dy);
1160 /* B = Join(B, FindStems(currx, curry)); */
1161 B = Join(B, FindStems(currx, curry, dx, dy));
1164 path = Join(path, Line(B));
1167 /* |- dx1 dy1 dx2 dy2 dx3 dy3 RRCURVETO |- */
1168 /* Relative RCURVETO, equivalent to dx1 dy1 */
1169 /* (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) */
1170 /* (dy1+dy2+dy3) RCURVETO in PostScript */
1171 static RRCurveTo(dx1, dy1, dx2, dy2, dx3, dy3)
1172 double dx1, dy1, dx2, dy2, dx3, dy3;
1174 struct segment *B, *C, *D;
1176 IfTrace4((FontDebug), "RRCurveTo %f %f %f %f ", &dx1, &dy1, &dx2, &dy2);
1177 IfTrace2((FontDebug), "%f %f\n", &dx3, &dy3);
1179 B = Loc(CharSpace, dx1, dy1);
1180 C = Loc(CharSpace, dx2, dy2);
1181 D = Loc(CharSpace, dx3, dy3);
1184 /* For a Bezier curve, we apply the full hint value to
1185 the Bezier C point (and thereby D point). */
1186 currx += dx1 + dx2 + dx3;
1187 curry += dy1 + dy2 + dy3;
1188 /* C = Join(C, FindStems(currx, curry)); */
1189 C = Join(C, FindStems(currx, curry, dx3, dy3));
1192 /* Since XIMAGER is not completely relative, */
1193 /* we need to add up the delta values */
1195 C = Join(C, Dup(B));
1196 D = Join(D, Dup(C));
1198 path = Join(path, Bezier(B, C, D));
1201 /* - CLOSEPATH |- */
1202 /* Closes a subpath WITHOUT repositioning the */
1204 static DoClosePath()
1206 struct segment *CurrentPoint;
1208 IfTrace0((FontDebug), "DoClosePath\n");
1209 CurrentPoint = Phantom(path);
1210 path = ClosePath(path);
1211 path = Join(Snap(path), CurrentPoint);
1214 /* subr# CALLSUBR - */
1215 /* Calls a CharString subroutine with index */
1216 /* subr# from the Subrs array */
1217 static CallSubr(subrno)
1220 IfTrace1((FontDebug), "CallSubr %d\n", subrno);
1221 if ((subrno < 0) || (subrno >= SubrsP->len))
1222 Error0("CallSubr: subrno out of range\n");
1223 PushCall(CharStringP, strindex, r);
1224 CharStringP = &SubrsP->data.arrayP[subrno];
1229 /* Returns from a Subrs array CharString */
1230 /* subroutine called with CALLSUBR */
1233 IfTrace0((FontDebug), "Return\n");
1234 PopCall(&CharStringP, &strindex, &r);
1238 /* Finishes a CharString outline */
1239 /* Executes SETCHACHEDEVICE using a bounding box */
1240 /* it computes directly from the character outline */
1241 /* and using the width information acquired from a previous */
1242 /* HSBW or SBW. It then calls a special version of FILL */
1243 /* or STROKE depending on the value of PaintType in the */
1244 /* font dictionary */
1247 IfTrace0((FontDebug), "EndChar\n");
1249 /* There is no need to compute and set bounding box for
1250 the cache, since XIMAGER does that on the fly. */
1252 /* Perform a Closepath just in case the command was left out */
1253 path = ClosePath(path);
1255 /* Set character width */
1256 path = Join(Snap(path), Loc(CharSpace, escapementX, escapementY));
1260 /* |- dx dy RMOVETO |- */
1261 /* Behaves like RMOVETO in PostScript */
1262 static RMoveTo(dx,dy)
1267 IfTrace2((FontDebug), "RMoveTo %f %f\n", &dx, &dy);
1269 B = Loc(CharSpace, dx, dy);
1274 /* B = Join(B, FindStems(currx, curry)); */
1275 B = Join(B, FindStems(currx, curry, 0.0, 0.0));
1278 path = Join(path, B);
1281 /* - DOTSECTION |- */
1282 /* Brackets an outline section for the dots in */
1283 /* letters such as "i", "j", and "!". */
1286 IfTrace0((FontDebug), "DotSection\n");
1287 InDotSection = !InDotSection;
1290 /* |- asb adx ady bchar achar SEAC |- */
1291 /* Standard Encoding Accented Character. */
1292 static Seac(asb, adx, ady, bchar, achar)
1293 double asb, adx, ady;
1294 unsigned char bchar, achar;
1297 struct segment *mypath;
1299 IfTrace4((FontDebug), "SEAC %f %f %f %d ", &asb, &adx, &ady, bchar);
1300 IfTrace1((FontDebug), "%d\n", achar);
1302 /* Move adx - asb, ady over and up from base char's sbpoint. */
1303 /* (We use adx - asb to counteract the accents sb shift.) */
1304 /* The variables accentoffsetX/Y modify sidebearingX/Y in Sbw(). */
1305 /* Note that these incorporate the base character's sidebearing shift by */
1306 /* using the current sidebearingX, Y values. */
1307 accentoffsetX = sidebearingX + adx - asb;
1308 accentoffsetY = sidebearingY + ady;
1310 /* Set path = NULL to avoid complaints from Sbw(). */
1313 /* Go find the CharString for the accent's code via an upcall */
1314 CharStringP = GetType1CharString(Environment, achar);
1322 if (!DoRead(&Code)) break;
1324 if (errflag) return;
1326 /* Copy snapped path to mypath and set path to NULL as above. */
1327 mypath = Snap(path);
1330 /* We must reset these to null now. */
1331 accentoffsetX = accentoffsetY = 0;
1333 /* go find the CharString for the base char's code via an upcall */
1334 CharStringP = GetType1CharString(Environment, bchar);
1345 if (!DoRead(&Code)) break;
1347 if (errflag) return;
1349 path = Join(mypath, path);
1353 /* |- sbx sby wx wy SBW |- */
1354 /* Set the left sidebearing point to (sbx,sby), */
1355 /* set the character width vector to (wx,wy). */
1356 static Sbw(sbx, sby, wx, wy)
1357 double sbx, sby, wx, wy;
1359 IfTrace4((FontDebug), "SBW %f %f %f %f\n", &sbx, &sby, &wx, &wy);
1361 escapementX = wx; /* Character width vector */
1364 /* Sidebearing values are sbx, sby args, plus accent offset from Seac(). */
1365 sidebearingX = sbx + accentoffsetX;
1366 sidebearingY = sby + accentoffsetY;
1368 path = Join(path, Loc(CharSpace, sidebearingX, sidebearingY));
1369 if (ProcessHints) {currx = sidebearingX; curry = sidebearingY;}
1372 /* num1 num2 DIV quotient */
1373 /* Behaves like DIV in the PostScript language */
1374 static double Div(num1, num2)
1377 IfTrace2((FontDebug), "Div %f %f\n", &num1, &num2);
1378 return(num1 / num2);
1382 The following four subroutines (FlxProc, FlxProc1, FlxProc2, and
1383 HintReplace) are C versions of the OtherSubrs Programs, which were
1384 were published in the Adobe Type 1 Font Format book.
1386 The Flex outline fragment is described by
1387 c1: (x0, y0) = c3: (x0, yshrink(y0)) or (xshrink(x0), y0)
1388 " (x1, y1) = " (x1, yshrink(y1)) or (xshrink(x1), y1)
1389 " (x2, y2) - reference point
1390 c2: (x0, y0) = c4: (x0, yshrink(y0)) or (xshrink(x0), y0)
1391 " (x1, y1) = " (x1, yshrink(y1)) or (xshrink(x1), y1)
1392 " (x2, y2) = " (x2, y2), rightmost endpoint
1393 c3: (x0, y0) - control point, 1st Bezier curve
1394 " (x1, y1) - control point, -"-
1395 " (x2, y2) - end point, -"-
1396 c4: (x0, y0) - control point, 2nd Bezier curve
1397 " (x1, y1) - control point, -"-
1398 " (x2, y2) - end point, -"-
1399 ep: (epY, epX) - final endpoint (should be same as c4: (x2, y2))
1400 idmin - minimum Flex height (1/100 pixel) at which to render curves
1403 #define dtransform(dxusr,dyusr,dxdev,dydev) { \
1404 register struct segment *point = Loc(CharSpace, dxusr, dyusr); \
1405 QueryLoc(point, IDENTITY, dxdev, dydev); \
1409 #define itransform(xdev,ydev,xusr,yusr) { \
1410 register struct segment *point = Loc(IDENTITY, xdev, ydev); \
1411 QueryLoc(point, CharSpace, xusr, yusr); \
1415 #define transform(xusr,yusr,xdev,ydev) dtransform(xusr,yusr,xdev,ydev)
1417 #define PaintType (0)
1419 #define lineto(x,y) { \
1420 struct segment *CurrentPoint; \
1421 double CurrentX, CurrentY; \
1422 CurrentPoint = Phantom(path); \
1423 QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY); \
1424 Destroy(CurrentPoint); \
1425 RLineTo(x - CurrentX, y - CurrentY); \
1428 #define curveto(x0,y0,x1,y1,x2,y2) { \
1429 struct segment *CurrentPoint; \
1430 double CurrentX, CurrentY; \
1431 CurrentPoint = Phantom(path); \
1432 QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY); \
1433 Destroy(CurrentPoint); \
1434 RRCurveTo(x0 - CurrentX, y0 - CurrentY, x1 - x0, y1 - y0, x2 - x1, y2 - y1); \
1437 #define xshrink(x) ((x - c4x2) * shrink +c4x2)
1438 #define yshrink(y) ((y - c4y2) * shrink +c4y2)
1440 #define PickCoords(flag) \
1441 if (flag) { /* Pick "shrunk" coordinates */ \
1442 x0 = c1x0; y0 = c1y0; \
1443 x1 = c1x1; y1 = c1y1; \
1444 x2 = c1x2; y2 = c1y2; \
1445 x3 = c2x0; y3 = c2y0; \
1446 x4 = c2x1; y4 = c2y1; \
1447 x5 = c2x2; y5 = c2y2; \
1448 } else { /* Pick original coordinates */ \
1449 x0 = c3x0; y0 = c3y0; \
1450 x1 = c3x1; y1 = c3y1; \
1451 x2 = c3x2; y2 = c3y2; \
1452 x3 = c4x0; y3 = c4y0; \
1453 x4 = c4x1; y4 = c4y1; \
1454 x5 = c4x2; y5 = c4y2; \
1457 /* FlxProc() = OtherSubrs[0]; Main part of Flex */
1458 /* Calling sequence: 'idmin epX epY 3 0 callothersubr' */
1459 /* Computes Flex values, and renders the Flex path, */
1460 /* and returns (leaves) ending coordinates on stack */
1461 static void FlxProc(c1x2, c1y2, c3x0, c3y0, c3x1, c3y1, c3x2, c3y2,
1462 c4x0, c4y0, c4x1, c4y1, c4x2, c4y2, epY, epX, idmin)
1464 double c3x0, c3y0, c3x1, c3y1, c3x2, c3y2;
1465 double c4x0, c4y0, c4x1, c4y1, c4x2, c4y2;
1470 double c1x0, c1y0, c1x1, c1y1;
1471 double c2x0, c2y0, c2x1, c2y1, c2x2, c2y2;
1473 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5;
1474 double cxx, cyx, cxy, cyy; /* Transformation matrix */
1477 double erosion = 1; /* Device parameter */
1478 /* Erosion may have different value specified in 'internaldict' */
1487 path = FlxOldPath; /* Restore previous path (stored in FlxProc1) */
1490 dmin = ABS(idmin) / 100.0; /* Minimum Flex height in pixels */
1492 c2x2 = c4x2; c2y2 = c4y2; /* Point c2 = c4 */
1494 yflag = FABS(c1y2 - c3y2) > FABS(c1x2 - c3x2); /* Flex horizontal? */
1496 QuerySpace(CharSpace, &cxx, &cyx, &cxy, &cyy); /* Transformation matrix */
1498 if (FABS(cxx) < 0.00001 || FABS(cyy) < 0.00001)
1499 flipXY = -1; /* Char on side */
1500 else if (FABS(cyx) < 0.00001 || FABS(cxy) < 0.00001)
1501 flipXY = 1; /* Char upright */
1503 flipXY = 0; /* Char at angle */
1505 if (yflag) { /* Flex horizontal */
1506 if (flipXY == 0 || c3y2 == c4y2) { /* Char at angle or Flex height = 0 */
1507 PickCoords(FALSE); /* Pick original control points */
1509 shrink = FABS((c1y2 - c4y2) / (c3y2 - c4y2)); /* Slope */
1511 c1x0 = c3x0; c1y0 = yshrink(c3y0);
1512 c1x1 = c3x1; c1y1 = yshrink(c3y1);
1513 c2x0 = c4x0; c2y0 = yshrink(c4y0);
1514 c2x1 = c4x1; c2y1 = yshrink(c4y1);
1516 dtransform(0.0, ROUND(c3y2-c1y2), &x, &y); /* Flex height in pixels */
1517 dY = FABS((flipXY == 1) ? y : x);
1518 PickCoords(dY < dmin); /* If Flex small, pick 'shrunk' control points */
1520 if (FABS(y2 - c1y2) > 0.001) { /* Flex 'non-zero'? */
1521 transform(c1x2, c1y2, &x, &y);
1529 dtransform(0.0, ROUND(y2-c1y2), &x, &y);
1530 dY = (flipXY == 1) ? y : x;
1534 dY = (dY < 0) ? -1 : 1;
1536 erode = PaintType != 2 && erosion >= 0.5;
1540 ey = CEIL(ey) - ey + FLOOR(ey);
1545 itransform(cx, ey, &x, &y);
1547 itransform(ey, cx, &x, &y);
1556 } else { /* Flex vertical */
1557 if (flipXY == 0 || c3x2 == c4x2) { /* Char at angle or Flex height = 0 */
1558 PickCoords(FALSE); /* Pick original control points */
1560 shrink = FABS((c1x2 - c4x2) / (c3x2 - c4x2)); /* Slope */
1562 c1x0 = xshrink(c3x0); c1y0 = c3y0;
1563 c1x1 = xshrink(c3x1); c1y1 = c3y1;
1564 c2x0 = xshrink(c4x0); c2y0 = c4y0;
1565 c2x1 = xshrink(c4x1); c2y1 = c4y1;
1567 dtransform(ROUND(c3x2 - c1x2), 0.0, &x, &y); /* Flex height in pixels */
1568 dX = FABS((flipXY == -1) ? y : x);
1569 PickCoords(dX < dmin); /* If Flex small, pick 'shrunk' control points */
1571 if (FABS(x2 - c1x2) > 0.001) {
1572 transform(c1x2, c1y2, &x, &y);
1579 dtransform(ROUND(x2-c1x2), 0.0, &x, &y);
1580 dX = (flipXY == -1) ? y : x;
1584 dX = (dX < 0) ? -1 : 1;
1586 erode = PaintType != 2 && erosion >= 0.5;
1590 ex = CEIL(ex) - ex + FLOOR(ex);
1595 itransform(cy, ex, &x, &y);
1597 itransform(ex, cy, &x, &y);
1608 if (x2 == x5 || y2 == y5) {
1611 curveto(x0, y0, x1, y1, x2, y2);
1612 curveto(x3, y3, x4, y4, x5, y5);
1614 } else { /* ProcessHints is off */
1615 PickCoords(FALSE); /* Pick original control points */
1616 curveto(x0, y0, x1, y1, x2, y2);
1617 curveto(x3, y3, x4, y4, x5, y5);
1624 /* FlxProc1() = OtherSubrs[1]; Part of Flex */
1625 /* Calling sequence: '0 1 callothersubr' */
1626 /* Saves and clears path, then restores currentpoint */
1627 static void FlxProc1()
1629 struct segment *CurrentPoint;
1631 CurrentPoint = Phantom(path);
1634 path = CurrentPoint;
1637 /* FlxProc2() = OtherSubrs[2]; Part of Flex */
1638 /* Calling sequence: '0 2 callothersubr' */
1639 /* Returns currentpoint on stack */
1640 static void FlxProc2()
1642 struct segment *CurrentPoint;
1643 double CurrentX, CurrentY;
1645 CurrentPoint = Phantom(path);
1646 QueryLoc(CurrentPoint, CharSpace, &CurrentX, &CurrentY);
1647 Destroy(CurrentPoint);
1649 /* Push CurrentPoint on fake PostScript stack */
1650 PSFakePush(CurrentX);
1651 PSFakePush(CurrentY);
1654 /* HintReplace() = OtherSubrs[3]; Hint Replacement */
1655 /* Calling sequence: 'subr# 1 3 callothersubr pop callsubr' */
1656 /* Reinitializes stem hint structure */
1657 static void HintReplace()
1659 /* Effectively retire the current stems, but keep them around for */
1660 /* revhint use in case we are in a stem when we replace hints. */
1661 currstartstem = numstems;
1663 /* 'subr#' is left on PostScript stack (for 'pop callsubr') */
1666 /* arg1 ... argn n othersubr# CALLOTHERSUBR - */
1667 /* Make calls on the PostScript interpreter (or call equivalent C code) */
1668 /* NOTE: The n arguments have been pushed on the fake PostScript stack */
1669 static CallOtherSubr(othersubrno)
1672 IfTrace1((FontDebug), "CallOtherSubr %d\n", othersubrno);
1674 switch(othersubrno) {
1675 case 0: /* OtherSubrs[0]; Main part of Flex */
1676 if (PSFakeTop < 16) Error0("CallOtherSubr: PSFakeStack low");
1679 PSFakeStack[0], PSFakeStack[1], PSFakeStack[2], PSFakeStack[3],
1680 PSFakeStack[4], PSFakeStack[5], PSFakeStack[6], PSFakeStack[7],
1681 PSFakeStack[8], PSFakeStack[9], PSFakeStack[10], PSFakeStack[11],
1682 PSFakeStack[12], PSFakeStack[13], PSFakeStack[14], PSFakeStack[15],
1683 (int) PSFakeStack[16]
1686 case 1: /* OtherSubrs[1]; Part of Flex */
1689 case 2: /* OtherSubrs[2]; Part of Flex */
1692 case 3: /* OtherSubrs[3]; Hint Replacement */
1695 default: { /* call OtherSubrs[4] or higher if PostScript is present */
1700 /* |- x y SETCURRENTPOINT |- */
1701 /* Sets the current point to (x,y) in absolute */
1702 /* character space coordinates without per- */
1703 /* forming a CharString MOVETO command */
1704 static SetCurrentPoint(x, y)
1707 IfTrace2((FontDebug), "SetCurrentPoint %f %f\n", &x, &y);
1713 /* The Type1Char routine for use by PostScript. */
1714 /************************************************/
1715 struct xobject *Type1Char(env, S, charstrP, subrsP, osubrsP, bluesP, modeP)
1721 struct blues_struct *bluesP; /* FontID's ptr to the blues struct */
1729 /* Make parameters available to all Type1 routines */
1731 CharSpace = S; /* used when creating path elements */
1732 CharStringP = charstrP;
1734 OtherSubrsP = osubrsP;
1739 /* compute the alignment zones */
1740 ComputeAlignmentZones();
1751 escapementX = escapementY = 0;
1752 sidebearingX = sidebearingY = 0;
1753 accentoffsetX = accentoffsetY = 0;
1754 wsoffsetX = wsoffsetY = 0; /* No shift to preserve whitspace. */
1755 wsset = 0; /* wsoffsetX,Y haven't been set yet. */
1758 if (!DoRead(&Code)) break;
1766 /* Clean up if an error has occurred */
1769 Destroy(path); /* Reclaim storage */
1770 path = NULL; /* Indicate that character could not be built */
1774 return((struct xobject *) path);