/*** ** ** Module: Hints ** ** Description: ** This is a module of the T1 to TT font converter. This is a ** sub-module of the T1 to TT data translator module. It deals ** with hints. Any part pf the T1 font that gets translated into ** TrueType instructions is done within this module. ** ** Author: Michael Jansson ** ** Created: 8/24/93 ** ***/ /**** INCLUDES */ /* General types and definitions. */ #include #include /* Special types and definitions. */ #include "titott.h" #include "trig.h" #include "types.h" #include "safemem.h" #include "metrics.h" #include "t1msg.h" /* Module dependent types and prototypes. */ #include "trans.h" #include "hints.h" #include "ttprog.h" /***** CONSTANTS */ #define VERSION_SELECTOR 1 /* GetInfo[] selector for version number. */ #define VERSION_1_5 33 /* Version 1.5 of Windows TrueType rasterizer. */ #define STEMSNAPARGS 6 /* Number of args of the CreateStem TTFUN. */ #ifdef SYMETRICAL_REDUCTION #define MIN_REDUCTION 4 /* Min reduction of the diag. cntrl. */ #endif #define REDUCTION_C1 10 /* Min reduction, second method. */ #define STACKINC 500 /* Stack increment for arg-stack + prep. */ #define TARGSIZE 100 /* Size of temporary argument stack. */ #define TTFLEXSIZE 9 /* Largest size of a flex, w/o the points. */ #define TMP_TWILIGHTS 2 #define TWILIGHTS_PER_STEM 4 #define LEFTSTEM 1 #define RIGHTSTEM 2 #define SECONDPAIR 2 #define MAXRANGE 15 #define MAXEXTR 60 /* Max num of IP buckets. */ #define UNDEF -1 #define STDV_CVT 1 #define STDH_CVT 2 #define SNAPV_CVT(v) (v+3) #define SNAPH_CVT(t1m, v) (t1m->snapv_cnt+3+v) /* External leading hint programs. */ static const UBYTE roman_hints[] = { /* Magic cookie. */ op_pushb1 + 4, 66, 3, 8, 2, 16, op_clear, op_svcta | SUBOP_Y, op_pushb1, 3, /* Push 2pnt, in sub-pels. */ op_mppem, op_mps, op_div, op_pushb1, 128, op_mul, /* Push InternalLeading, in sub-pels. */ op_pushb1+1, 2, 1, op_md, op_sub, /* Push MAX(2pnt - i-leading, 0) */ op_pushb1, 0, op_max, /* Add the external leading to the Ascent height. */ op_shpix, }; static const UBYTE swiss_hints[] = { /* Magic cookie. */ op_pushb1 + 4, 66, 3, 8, 2, 16, op_clear, op_svcta | SUBOP_Y, op_pushb1, 3, /* 0<=height<=12.5 */ op_mps, op_pushw1, HIBYTE(800), LOBYTE(800), /* 12.5 pnt */ op_gt, op_if, /* Push 2pnt, in sub-pels. */ op_mppem, op_mps, op_div, op_pushb1, 128, op_mul, op_else, /* 12.5 < height <= 13.5 */ op_mps, op_pushw1, HIBYTE(864), LOBYTE(864), /* 13.5 pnt */ op_gt, op_if, /* Push 3pnt, in sub-pels. */ op_mppem, op_pushb1, 1, op_div, op_mps, op_div, op_pushb1, 192, op_mul, op_else, /* Push 4pnt, in sub-pels. */ op_mppem, op_pushb1, 1, op_div, op_mps, op_div, op_pushw1, HIBYTE(256), /* LOBYTE(256) */ 0, op_mul, op_eif, op_eif, /* Push InternalLeading, in sub-pels. */ op_pushb1+1, 2, 1, op_md, op_sub, op_dup, /* Push MAX(?pnt - i-leading, 0) */ op_pushb1, 0, op_max, /* Add the external leading to the Ascent height. */ op_shpix, }; /* Pre-program. */ static const UBYTE PrepProg[] = { op_pushw1, 0x01, 0xff, op_scanctrl, op_pushb1, 1, op_rcvt, op_pushb1, 128, op_lt, op_if, op_pushb1 + 1, 4, 0, op_scantype, op_scantype, op_else, op_pushb1 + 1, 5, 1, op_scantype, op_scantype, op_eif, }; /***** LOCAL TYPES */ /* Used for associating points to stems. */ typedef struct { short from; short to; } Range; /* Zone bucket - Used for grid fitting a stem that may have been divided into several stem instructions due to hint replacement. */ typedef struct TTStem { funit side1; funit side2; short rp1; short rp2; short ref; enum aligntype align; Range range[MAXRANGE]; short cnt; } TTStem; /***** MACROS */ /* General macros. */ #define Trans3X TransX #define TransRX TransY #define CLOSETO(v1, v2, eps) (ABS((v1)-(v2))<=eps) #define CHECK_ARGSIZE(args, ta, num, asize) /* Check argument stack. */ \ /*lint -e571 -e644 */if (((ta)+(int)(num))>(asize)) { \ short *newarg = NULL;\ if ((newarg = Realloc(args, sizeof(short)*(USHORT)(ta+num+STACKINC)))==NULL) { \ Free(args); \ LogError(MSG_ERROR, MSG_NOMEM, NULL);\ return 0;\ } else {\ args = newarg;\ asize = (short)(ta+num+STACKINC);\ /*line +e571 +e644 */ }\ } #define CHECK_PREPSIZE(prep, tp, num, psize) /* Check prep size. */ \ if (((tp)+(num))>(psize)) { \ UBYTE *newprep = NULL;\ if ((newprep = Realloc(prep, tp+num+STACKINC))==NULL) { \ Free(prep); \ LogError(MSG_ERROR, MSG_NOMEM, NULL);\ return 0;\ } else {\ prep = newprep;\ psize = (short)(tp+num+STACKINC);\ }\ } /***** STATIC FUNCTIONS */ /*** ** Function: ConvertFlex ** ** Description: ** Convert a T1 flex hint into a TrueType IP[] ** intruction sequence that will reduce a flex ** that is flatter than a given height. ***/ static errcode ConvertFlex(const struct T1Metrics *t1m, const Flex *flexRoot, const short *ttpnts, UBYTE *pgm, short *pc, short *args, short *pcd, short *marg) { errcode status = SUCCESS; int cis, last_cis = UNDEF; char dir, last_dir = 0; short targ[TARGSIZE]; funit height, diff; const Flex *flex; short ta = 0; int num = 0; /* Return to the glyph zone. */ if (flexRoot) { pgm[(*pc)++] = op_szps; args[(*pcd)++] = 1; } for (flex=flexRoot; flex; flex=flex->next) { /* Points lost in ConvertOutline? */ if (ttpnts[flex->start]==UNDEF || ttpnts[flex->mid]==UNDEF || ttpnts[flex->end]==UNDEF) { LogError(MSG_WARNING, MSG_FLEX, NULL); continue; } /* Vertical or horizontal flex? */ if (ABS(flex->midpos.x-flex->pos.x) < ABS(flex->midpos.y-flex->pos.y)) { dir = SUBOP_Y; height = TransY(t1m, (funit)(flex->startpos.y - flex->pos.y)); diff = TransY(t1m, (funit)(flex->midpos.y - flex->startpos.y)); } else { dir = SUBOP_X; height = TransX(t1m, (funit)(flex->startpos.x - flex->pos.x)); diff = TransX(t1m, (funit)(flex->midpos.x - flex->startpos.x)); } /* Skip flex without depth. */ if (diff==0) continue; cis = (int)((long)flex->civ * (long)GetUPEM(t1m) / 100 / ABS(diff)); if (cis!=last_cis || dir!=last_dir || (ta+TTFLEXSIZE+(ttpnts[flex->end]-ttpnts[flex->start]))>=TARGSIZE) { if (last_cis!=UNDEF) { AssembleArgs(targ, ta, pgm, pc); while(num--) pgm[(*pc)++] = op_call; pgm[(*pc)++] = op_eif; ta = 0; } pgm[(*pc)++] = (UBYTE)(op_svcta | dir); pgm[(*pc)++] = op_mppem; pgm[(*pc)++] = op_gt; pgm[(*pc)++] = op_if; args[(*pcd)++] = (short)(cis+1); num = 0; } status = EmitFlex(targ, &ta, height, ttpnts[flex->start], ttpnts[flex->mid], ttpnts[flex->end]); last_dir = dir; last_cis = cis; num++; if (status!=SUCCESS) { SetError(status = TTSTACK); break; } } if (num) { AssembleArgs(targ, ta, pgm, pc); while(num--) pgm[(*pc)++] = op_call; pgm[(*pc)++] = op_eif; } if ((*marg)<2) (*marg) = 2; return status; } /*** ** Function: GetSnapV ** ** Description: ** Return the closest snap width entry. ***/ static short GetSnapV(const struct T1Metrics *t1m, const funit width) { USHORT dist = SHRT_MAX; USHORT j = 0; USHORT i; for (i=0; isnapv_cnt; i++) { if (ABS(width-t1m->stemsnapv[i])<(short)dist) { dist = (USHORT)ABS(width-t1m->stemsnapv[i]); j = i; } } if (dist==SHRT_MAX) return UNDEF; return (short)j; } /*** ** Function: GetSnapH ** ** Description: ** Return the closest snap width entry. ***/ static short GetSnapH(const struct T1Metrics *t1m, const funit width) { USHORT dist = SHRT_MAX; USHORT j = 0; USHORT i; for (i=0; isnaph_cnt; i++) { if (ABS(width-t1m->stemsnaph[i])<(short)dist) { dist = (USHORT)ABS(width-t1m->stemsnaph[i]); j = i; } } if (dist==SHRT_MAX) return UNDEF; return (short)j; } /*** ** Function: PosX ** ** Description: ** This is a call-back function used by ** Interpolate. ***/ static funit PosX(const Point pnt) { return pnt.x; } /*** ** Function: PosY ** ** Description: ** This is a call-back function used by ** Interpolate. ***/ static funit PosY(const Point pnt) { return pnt.y; } /*** ** Function: InRange ** ** Description: ** This is function determines if a point is ** within range of a hint zone. ***/ static boolean InRange(const short pnt, const Range *range, const short cnt) { short k; for (k=0; k=pnt || range[k].to==ENDOFPATH)) break; } return (boolean)(k != cnt); } /*** ** Function: BoundingStems ** ** Description: ** Determines what stems are located to the ** left and to the right of a point on the ** outline, given its position. ** ***/ static short BoundingStems(short pnt, const short max_pnt, const funit pos, const TTStem *stems, const short cnt, short *left, short *right) { funit min, max; short i; max = SHRT_MAX; min = 1-SHRT_MAX; (*right) = UNDEF; (*left) = UNDEF; do { for (i=0; imin) && InRange(pnt, stems[i].range, stems[i].cnt)) { min = stems[i].side1; (*left) = (short)i; } /* Is stem to the right and defined for the point. */ if ((stems[i].side2>=pos) && (stems[i].side2cnt; k++) { if ((stem->range[k].from<=pnt) && (stem->range[k].to>=pnt || stem->range[k].to==ENDOFPATH)) break; } return (short)((k==stem->cnt || stem->range[k].to==ENDOFPATH) ? SHRT_MAX : stem->range[k].to); } /*** ** Function: AddToBucket ** ** Description: ** This function will add a point, that ** is located between two stems, into a ** bucket that represents an interpolation ** zone. ***/ static short AddToBucket(Extremas *extr, short xcnt, const short pnt, const funit left, const funit right, const TTStem *stems) { short rp1, rp2; short tmp, j; /* Pick the reference points (which are located in the twilight zone). */ if (left!=UNDEF) rp1 = stems[left].rp2; else rp1 = UNDEF; if (right!=UNDEF) rp2 = stems[right].rp1; else rp2 = UNDEF; /* Normalize the reference points. */ tmp = rp1; rp1 = (short)MIN(rp1, rp2); rp2 = (short)MAX(tmp, rp2); /* Create/Fill IP bucket. */ for (j=0; jnext) { first = BoundingStems(tot, (short)(tot+(short)path->count), Position(path->pts[0]), stems, cnt, &left, &right); if (first==tot+(short)path->count) { tot = (short)(tot + path->count); continue; } new_seq = PickSequence(left, right, tot, stems); PickSides(left, right, &left_side, &right_side, stems); max = 1-SHRT_MAX/2; min_pnt = UNDEF; max_pnt = UNDEF; min = SHRT_MAX/2; prev_pnt = FALSE; prev_pos = UNDEF; prev_stem = UNDEF; for (i = (short)(first-tot); i<(short)path->count; i++) { if (OnCurve(path->onoff, i)) { pos = Position(path->pts[i]); n = (short)(i+tot); /* Have we crossed over a stem side. */ if ((prev_stem!=RIGHTSTEM && pos<=left_side && max_pnt!=UNDEF) || (prev_stem!=LEFTSTEM && pos>=right_side && min_pnt!=UNDEF)) { if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) { pnt = max_pnt; prev_pos = max; } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) { pnt = min_pnt; prev_pos = min; } xcnt = AddToBucket(extr, xcnt, ttpnts[pnt], left, right, stems); max = 1-SHRT_MAX/2; min = SHRT_MAX/2; max_pnt = UNDEF; min_pnt = UNDEF; prev_pnt = TRUE; } /* Crossing the side of a stem. */ if ((pos>=right_side) || (pos<=left_side)) { if (posnew_seq) || (pos>=right_side) || (pos<=left_side)) { first = BoundingStems(n, (short)(path->count+tot), pos, stems, cnt, &left, &right); if (left==UNDEF && right==UNDEF) break; i = (short)(i + first - n); new_seq = PickSequence(left, right, n, stems); PickSides(left, right, &left_side, &right_side, stems); max = 1-SHRT_MAX/2; min = SHRT_MAX/2; max_pnt = UNDEF; min_pnt = UNDEF; } /* Is the point on the side of the stem? */ if (CLOSETO(pos,left_side,2) || CLOSETO(pos,right_side,2)) { if (!prev_pnt || !CLOSETO(prev_pos, pos, 2)) { if (CLOSETO(pos, right_side, 2) || CLOSETO(pos, left_side, 2)) { pnt = (short)n; prev_pos = pos; } else if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) { pnt = max_pnt; prev_pos = max; max_pnt = UNDEF; } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) { pnt = min_pnt; prev_pos = min; min_pnt = UNDEF; } xcnt = AddToBucket(extr, xcnt, ttpnts[pnt], left, right, stems); } prev_pnt = TRUE; prev_pos = pos; } else { prev_pnt = FALSE; /* New extremum candidate? */ if (pos>max) { max = pos; max_pnt = (short)n; } if (poscount); } return xcnt; } /*** ** Function: CollectObliquePoints ** ** Description: ** This function performs the same task as ** the "CollectPoint" function, with the ** exception that the outline is known to ** be sheared. Some of the logics ** is changed, bacause the IUP[] instruction ** and some IP instruction will not behave ** the same as in a non-sheared font. ** This differance applies only to vertical ** stems (hints resulting in horizontal motion of ** of points). ***/ static short CollectObliquePoints(const Outline *orgpaths, const short *ttpnts, TTStem *stems, short cnt, Extremas *extr, funit (*Position)(const Point)) { const Outline *path; short xcnt = 0; short i,tot; short prev_stem; funit pos; short left, right; funit left_side, right_side; funit max, min; short max_pnt, min_pnt; short new_seq, n; short first; short pnt = UNDEF; tot = 0; for (path=orgpaths; path; path=path->next) { first = BoundingStems(tot, (short)(tot+path->count), Position(path->pts[0]), stems, cnt, &left, &right); if (first==tot+(short)path->count) { tot = (short)(tot + path->count); continue; } new_seq = PickSequence(left, right, tot, stems); PickSides(left, right, &left_side, &right_side, stems); max = 1-SHRT_MAX/2; min_pnt = UNDEF; max_pnt = UNDEF; min = SHRT_MAX/2; prev_stem = UNDEF; for (i = (short)(first-tot); i<(short)path->count; i++) { if (OnCurve(path->onoff, i)) { pos = Position(path->pts[i]); n = (short)(i+tot); /* Have we crossed over a stem side. */ if ((prev_stem!=RIGHTSTEM && pos<=left_side && max_pnt!=UNDEF) || (prev_stem!=LEFTSTEM && pos>=right_side && min_pnt!=UNDEF)) { if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) { pnt = max_pnt; } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) { pnt = min_pnt; } max = 1-SHRT_MAX/2; min = SHRT_MAX/2; max_pnt = UNDEF; min_pnt = UNDEF; } /* Crossing the side of a stem. */ if ((pos>=right_side) || (pos<=left_side)) { if (posnew_seq) || (pos>=right_side) || (pos<=left_side)) { first = BoundingStems(n, (short)(path->count+tot), pos, stems, cnt, &left, &right); if (left==UNDEF && right==UNDEF) break; i = (short)(i + first - n); new_seq = PickSequence(left, right, n, stems); PickSides(left, right, &left_side, &right_side, stems); max = 1-SHRT_MAX/2; min = SHRT_MAX/2; max_pnt = UNDEF; min_pnt = UNDEF; } /* Is the point on the side of the stem? */ if (CLOSETO(pos,left_side,2) || CLOSETO(pos,right_side,2)) { if (CLOSETO(pos, right_side, 2)) { pnt = (short)n; if (stems[right].side1==right_side) xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt], stems[right].rp1); else xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt], stems[right].rp2); } else if (CLOSETO(pos, left_side, 2)) { pnt = (short)n; if (stems[left].side1==left_side) xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt], stems[left].rp1); else xcnt = AddSidePntToBucket(extr, xcnt, ttpnts[pnt], stems[left].rp2); } else if (prev_stem!=RIGHTSTEM && max_pnt!=UNDEF) { pnt = max_pnt; max_pnt = UNDEF; } else if (prev_stem!=LEFTSTEM && min_pnt!=UNDEF) { pnt = min_pnt; min_pnt = UNDEF; } } else { /* New extremum candidate? */ if (pos>max) { max = pos; max_pnt = (short)n; } if (poscount); } return xcnt; } /*** ** Function: AddRange ** ** Description: ** This function adds a point range to ** a stem bucket. ***/ static void AddRange(TTStem *stem, const short i1, const short i2) { short i; /* Check if a prior range can be extended. */ if (i2!=ENDOFPATH) { for (i=0; icnt; i++) { if (stem->range[i].from == i2+1) break; } } else { i = stem->cnt; } if (i==stem->cnt) { if (stem->cntrange[stem->cnt].from = i1; stem->range[stem->cnt].to = i2; stem->cnt++; } else { LogError(MSG_WARNING, MSG_REPLC, NULL); } } else { stem->range[i].from = i1; } } /*** ** Function: CreateStemBuckets ** ** Description: ** This function will create stem buckets. ** Several duplicated T1 stem instructions ** may be mapped to the same bucket. ***/ static short CreateStemBuckets(Stem *stemRoot, Stem3 *stem3Root, TTStem **result) { Stem3 *stem3, *stm3; Stem *stem, *stm; TTStem *stems = NULL; short i, j; short cnt; short tzpnt = TMPPNT1+1; /* Count the stems. */ cnt = 0; (*result) = NULL; for (stem3=stem3Root; stem3; stem3=stem3->next) { /* Skip obsolete stems. */ if (stem3->stem1.i2 == NORANGE) continue; /* Look for a duplicate. */ for (stm3=stem3Root; stm3!=stem3; stm3=stm3->next) { if (stm3->stem1.offset==stem3->stem1.offset && stm3->stem2.offset==stem3->stem2.offset && stm3->stem3.offset==stem3->stem3.offset) break; } /* Count this stem if it is not a duplicate. */ if (stm3==stem3) cnt = (short)(cnt + 3); } for (stem=stemRoot; stem; stem=stem->next) { /* Skip obsolete stems. */ if (stem->i2 == NORANGE) continue; /* Look for a duplicate. */ for (stm=stemRoot; stm!=stem; stm=stm->next) { if (stm->offset==stem->offset && stm->width==stem->width) break; } /* Don't count this stem if it is a duplicate. */ if (stm==stem) cnt++; } /* Initiate them. */ if (cnt) { if ((stems = Malloc(sizeof(TTStem)*(USHORT)cnt))==NULL) { errcode status; SetError(status=NOMEM); return status; } i = (short)(cnt-1); /* Initiate the buckets for the stem3s */ for (stem3=stem3Root; stem3; stem3=stem3->next) { /* Skip obsolete stems. */ if (stem3->stem1.i2 == NORANGE) continue; /* Skip if bucket exist for this stem already. */ for (j=(short)(i+1); jstem1.offset && stems[j].side2==(stem3->stem1.offset+stem3->stem1.width)) break; } if (j==cnt) { /* The rightmost stem is positioned w.r.t. to the middle. */ stems[i].side1 = stem3->stem1.offset; stems[i].side2 = stem3->stem1.width + stem3->stem1.offset; stems[i].align = at_relative2; stems[i].ref = (short)(i-2); stems[i].rp1 = tzpnt++; stems[i].rp2 = tzpnt++; stems[i].cnt = 1; stems[i].range[0].from = stem3->stem1.i1; stems[i].range[0].to = stem3->stem1.i2; tzpnt+=2; i--; /* The leftmost stem is positioned w.r.t. to the middle. */ stems[i].side1 = stem3->stem3.offset; stems[i].side2 = stem3->stem3.width + stem3->stem3.offset; stems[i].align = at_relative1; stems[i].ref = (short)(i-1); stems[i].rp1 = tzpnt++; stems[i].rp2 = tzpnt++; stems[i].cnt = 1; stems[i].range[0].from = stem3->stem1.i1; stems[i].range[0].to = stem3->stem1.i2; tzpnt+=2; i--; /* The middle stem is centered. */ stems[i].side1 = stem3->stem2.offset; stems[i].side2 = stem3->stem2.width + stem3->stem2.offset; stems[i].align = at_centered; stems[i].rp1 = tzpnt++; stems[i].rp2 = tzpnt++; stems[i].cnt = 1; stems[i].range[0].from = stem3->stem1.i1; stems[i].range[0].to = stem3->stem1.i2; tzpnt+=2; i--; } else { AddRange(&stems[j-0], stem3->stem1.i1, stem3->stem1.i2); AddRange(&stems[j-1], stem3->stem3.i1, stem3->stem3.i2); AddRange(&stems[j-2], stem3->stem2.i1, stem3->stem2.i2); } } /* Initiate the buckets for the stems. */ for (stem=stemRoot; stem; stem=stem->next) { /* Skip obsolete stems. */ if (stem->i2 == NORANGE) continue; /* Skip if bucket exist for this stem already. */ for (j=(short)(i+1); j<(short)cnt; j++) { if (stems[j].side1==stem->offset && stems[j].side2==(stem->offset+stem->width)) break; } /* Initiate new bucket: Plain vstems and hstems are centered by default. Some hstems may be top- or bottom-aligen at a latter point. Some stems may be positioned w.r.t. another vstem if they overlapp and the RELATIVESTEMS compiler flag is turned on. */ if (j==cnt) { stems[i].side1 = stem->offset; stems[i].side2 = stem->width + stem->offset; stems[i].align = at_centered; stems[i].rp1 = tzpnt++; stems[i].rp2 = tzpnt++; stems[i].cnt = 1; stems[i].range[0].from = stem->i1; stems[i].range[0].to = stem->i2; tzpnt+=2; i--; } else { AddRange(&stems[j], stem->i1, stem->i2); } } /* This happens if two stems are defined for the same hint replacement region and the same position, which is an Adobe Type 1 font error (broken font). The converter will recover by ignoring redundant stems. */ if (i!=-1) { /* LogError(MSG_STEM3); */ for (j=0; j<=i; j++) { stems[j].cnt = 0; } } } (*result) = stems; return (short)cnt; } /*** ** Function: ResolveRelativeStem ** ** Description: ** This function decides if two stems should ** be aligned side1->side1, side2->side2, ** side1->side2 or side2->side1. ** Stem are positition in relation to each ** other for two reasons: They overlapp, they ** are aligned side by side or they are ** members of a stem3 hint. ***/ static void ResolveRelativeStem(TTStem *ref, TTStem *cur) { /* SIDE1->SIDE2 */ if (cur->side1==ref->side2) { cur->ref = ref->rp2; cur->align = at_relative1; /* SIDE1->SIDE2 */ } else if (cur->side2==ref->side1) { cur->ref = ref->rp1; cur->align = at_relative2; /* SIDE1->SIDE1 */ } else if ((cur->side1>ref->side1) && ((cur->side1-ref->side1+10)>= (cur->side2-ref->side2))) { cur->ref = ref->rp1; cur->align = at_relative1; /* SIDE2->SIDE2 */ } else { cur->ref = ref->rp2; cur->align = at_relative2; } } /*** ** Function: ConvertVStems ** ** Description: ** This function translate vstem and vstem3 to TT instructions. ***/ static errcode ConvertVStems(struct T1Metrics *t1m, const Hints *hints, const Outline *orgpaths, const short *ttpnts, UBYTE *pgm, short *pc_ptr, short *args, short *pcd_ptr, USHORT *twilight_ptr) { Extremas extr[MAXEXTR]; short xcnt = 0; errcode status = SUCCESS; short pc = *pc_ptr; short pcd = *pcd_ptr; TTStem *stems = NULL; short i; short cnt; /* Create the buckets. */ if ((cnt = CreateStemBuckets(hints->vstems, hints->vstems3, &(stems)))==NOMEM) { status = NOMEM; } else { /* Update Max num of twilight points. */ if ((cnt*TWILIGHTS_PER_STEM+TMP_TWILIGHTS) > (long)(*twilight_ptr)) (*twilight_ptr) = (USHORT)(cnt * TWILIGHTS_PER_STEM + TMP_TWILIGHTS); if (cnt && stems) { #if RELATIVESTEMS /* Do counter- and overlappning stem control? */ for (i=0; i stems[j].side2) || (stems[i].side2 + (funit)prox < stems[j].side1)) && (ABS(stems[i].side2-stems[i].side1- (stems[j].side2-stems[j].side1)) < prox2 || (short)(stems[i].side1 > stems[j].side2) != (short)(stems[i].side2 < stems[j].side1))) break; } if (i!=j) { if (stems[j].side1 < stems[i].side1) stems[i].align = at_relative1; else stems[i].align = at_relative2; stems[i].ref = j; } } } #endif /** Vertical stem hints */ EmitVerticalStems(pgm, &pc, args, &pcd); /* Handle sheared fonts by settin the projection vector to the italic angle. The TT instructions for the T1 hints can handle any projection vector. */ if (t1m->fmatrix!=DEFAULTMATRIX && GetFontMatrix(t1m)[2]!=0) { Point pt; pt.x = 0; pt.y = 1000; TransAllPoints(t1m, &pt, (short)1, GetFontMatrix(t1m)); SetProjection(pgm, &pc, args, &pcd, pt.x, pt.y); } /* Convert the buckets into instructions. */ for (i=0; ihstems, hints->hstems3, &(stems)); if (cnt==NOMEM) return NOMEM; /* Update Max num of twilight points. */ if ((USHORT)(cnt*TWILIGHTS_PER_STEM+TMP_TWILIGHTS) > (*twilight_ptr)) (*twilight_ptr) = (USHORT)(cnt * TWILIGHTS_PER_STEM + TMP_TWILIGHTS); #if RELATIVESTEMS /* Do counter- and overlappning stem control? */ for (i=0; i stems[j].side2) || (stems[i].side2 + (funit)prox < stems[j].side1)) && (ABS(stems[i].side2-stems[i].side1- (stems[j].side2-stems[j].side1)) < prox2 || (short)(stems[i].side1 > stems[j].side2) != (short)(stems[i].side2 < stems[j].side1))) break; } if (i!=j) { if (stems[j].side1 < stems[i].side1) stems[i].align = at_relative1; else stems[i].align = at_relative2; stems[i].ref = j; } } } #endif /* Do alignment control. */ for (i=0; iflex, ttpnts, pgm, &pc, args, &pcd, &marg); /******************** * Adjust diagonals * Do not reduce if dominant vertical stem width is more than * 2.0 pels at 11PPEm and above. This occurs when: * 1) StdVW > 187 * 2) StdVW < 100 and ForceBold = TRUE **/ if ((ForceBold(t1m)==1 && GetStdVW(t1m)>100 && GetStdVW(t1m)<187) || (ForceBold(t1m)==0 && GetStdVW(t1m)<187)) narg = ReduceDiagonals(paths, pgm, &pc, args, &pcd); if (narg>marg) marg = narg; if (pc>PGMSIZE) { SetError(status = TTSTACK); } if (pcd>ARGSIZE) { SetError(status = ARGSTACK); } /* Allocate the gpgm */ (*gpgm) = NULL; (*num) = 0; (*stack) = 0; if (status==SUCCESS) { if (pc) { if (((*gpgm) = Malloc((USHORT)(pc+pcd*3)))==NULL) { SetError(status = NOMEM); } else { /* Assemble the arguments for the instructions */ cnt = 0; AssembleArgs(args, pcd, (*gpgm), &cnt); memcpy(&(*gpgm)[cnt], pgm, (USHORT)pc); (*num) = (USHORT)(cnt + pc); (*stack) = (USHORT)(pcd + marg); } } } return status; } /*** ** Function: BuildPreProgram ** ** Description: ** This function builds the pre-program that will compute ** the CVT and storage entries for the TT stem hint ** instructions to work. ***/ USHORT BuildPreProgram(const struct T1Metrics *t1m, const WeightControl *weight, Blues *blues, AlignmentControl *align, UBYTE **glob_prep, const int prepsize, USHORT *maxstack) { UBYTE *prep = (*glob_prep); short *args = NULL; short ta, tp = 0; USHORT i, j; long shift; funit stdvw, stdhw; short cis; funit std_width; USHORT std_tres; funit min_dist; short k; short argsize = ARGSIZE; short psize = (short)prepsize; /* Allocate work space. */ if ((args=Malloc(sizeof(args[0])*(USHORT)argsize))==NULL) { LogError(MSG_ERROR, MSG_NOMEM, NULL); } else { /* Copy the standard pre-program. */ memcpy(prep, PrepProg, sizeof(PrepProg)); tp = sizeof(PrepProg); (*maxstack) = 0; /********** * Compute Blue values. */ prep[tp++] = op_pushb1; prep[tp++] = blues->blueScale; prep[tp++] = op_mppem; prep[tp++] = op_lt; prep[tp++] = op_if; prep[tp++] = op_pushb1; prep[tp++] = ONEPIXEL; prep[tp++] = op_smd; prep[tp++] = op_pushb1; prep[tp++] = TWILIGHT; prep[tp++] = op_szps; prep[tp++] = op_svcta | SUBOP_Y; prep[tp++] = op_rtg; /***********************/ /*** ABOVE BlueScale ***/ /***********************/ /* Align the top zones. */ for (i=0; iblue_cnt/2; i++) { min_dist = SHRT_MAX; k = UNDEF; /*** Copy the FamilyBlue entries to the BlueValues if */ /*** below the Family cut in size. */ if (blues->fblue_cnt>0) { /* Do the cut in on FamilyBlue/BlueValue. */ k = MatchingFamily(blues->bluevalues[i*2], blues->familyblues, blues->fblue_cnt); min_dist = ABS(blues->bluevalues[i*2] - blues->familyblues[k]); /* Always FamilyBlue? */ if (min_dist) { cis = (short)(GetUPEM(t1m) / TransY(t1m, min_dist)); tp = (short)FamilyCutIn(prep, (USHORT)tp, cis); } /* Allocate a cvt if this family has not been used before. */ if (blues->family_cvt[k/2]==UNDEF_CVT) { blues->family_cvt[k/2] = align->cvt; align->cvt += 2; } ta = 2; CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize); for (j=0; jtop[i].cnt; j++) { args[ta++] = (short)align->top[i].pos[j].cvt; } CHECK_PREPSIZE(prep, tp, 2*ta+10, psize); tp = (short)CopyFamilyBlue(prep, tp, args, ta); if ((ta+2)>(int)(*maxstack)) (*maxstack) = (USHORT)(ta+2); /* Set up the zone. */ tp = (short)SetZone(prep, (USHORT)tp, (short)(blues->family_cvt[k/2])); if (min_dist>0) prep[tp++] = op_else; } /*** Set up the zone. */ CHECK_PREPSIZE(prep, tp, STACKINC, psize); tp = (short)SetZone(prep, (USHORT)tp, (short)(align->top[i].blue_cvt)); if (k!=UNDEF && min_dist) { prep[tp++] = op_eif; } /*** Round and enforce overshoot. */ ta = 2; CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize); for (j=0; jtop[i].cnt; j++) { if ((align->top[i].pos[j].y - blues->bluevalues[i*2])*F8D8 > blues->blueShift) { args[ta++] = (short)align->top[i].pos[j].cvt; } } if (ta>2) { CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)AlignOvershoot(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; } ta = 2; CHECK_ARGSIZE(args, ta, align->top[i].cnt, argsize); for (j=0; jtop[i].cnt; j++) { if ((align->top[i].pos[j].y - blues->bluevalues[i*2])*F8D8 <= blues->blueShift) { args[ta++] = (short)align->top[i].pos[j].cvt; } } if (ta>2) { CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)AlignFlat(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)(ta+2); } } /* Align the bottom zones. */ for (i=0; ioblue_cnt/2; i++) { min_dist = SHRT_MAX; k = UNDEF; /*** Copy the FamilyBlue entries to the BlueValues if */ /*** below the Family cut in size. */ if (blues->foblue_cnt>0) { /* Do the cut in on FamilyBlue/BlueValue. */ k = MatchingFamily(blues->otherblues[i*2], blues->familyotherblues, blues->foblue_cnt); min_dist = ABS(blues->otherblues[i*2] - blues->familyotherblues[k]); /* Always FamilyBlue? */ if (min_dist) { cis = (short)(GetUPEM(t1m) / TransY(t1m, min_dist)); tp = (short)FamilyCutIn(prep, (USHORT)tp, cis); } /* Allocate a cvt if this family has not been used before. */ if (blues->familyother_cvt[k/2]==UNDEF_CVT) { blues->familyother_cvt[k/2] = align->cvt++; } ta = 2; CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize); for (j=0; jbottom[i].cnt; j++) { args[ta++] = (short)align->bottom[i].pos[j].cvt; } CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)CopyFamilyBlue(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; /* Set up the zone. */ tp = (short)SetZone(prep, (USHORT)tp, (short)blues->familyother_cvt[k/2]); if (min_dist>0) prep[tp++] = op_else; } /*** Set up the zone. */ tp = (short)SetZone(prep, (USHORT)tp, (short)align->bottom[i].blue_cvt); if (k!=UNDEF && min_dist) { prep[tp++] = op_eif; } /*** Round and enforce overshoot. */ ta = 2; CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize); for (j=0; jbottom[i].cnt; j++) { if ((align->bottom[i].pos[j].y - blues->otherblues[i*2+1])*F8D8 > blues->blueShift) { args[ta++] = (short)align->bottom[i].pos[j].cvt; } } if (ta>2) { CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)AlignOvershoot(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; } ta = 2; CHECK_ARGSIZE(args, ta, align->bottom[i].cnt, argsize); for (j=0; jbottom[i].cnt; j++) { if ((align->bottom[i].pos[j].y - blues->otherblues[i*2+1])*F8D8 <= blues->blueShift) { args[ta++] = (short)align->bottom[i].pos[j].cvt; } } if (ta>2) { CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)AlignFlat(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)(ta+2); } } /***********************/ /*** BELOW BlueScale ***/ /***********************/ prep[tp++] = op_else; /*** Align the top zones. */ for (i=0; iblue_cnt/2; i++) { /* Initiate */ min_dist = SHRT_MAX; k = UNDEF; /* switch between blues and family blues. */ if (blues->fblue_cnt) { /* Look for the closest family blue. */ k = MatchingFamily(blues->bluevalues[i*2], blues->familyblues, blues->fblue_cnt); min_dist = ABS(blues->bluevalues[i*2] - blues->familyblues[k]); /* Copy/Round the family overshoot position to the zone. */ if (min_dist) { cis = (short)(GetUPEM(t1m) / TransY(t1m, (funit)min_dist)); tp = (short)FamilyCutIn(prep, (USHORT)tp, cis); ta = 2; CHECK_ARGSIZE(args, ta, align->top[i].cnt*2, argsize); for (j=0; jtop[i].cnt; j++) { args[ta++] = (short)(blues->family_cvt[k/2] + 1); args[ta++] = (short)(align->top[i].pos[j].cvt); } CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)CopyZone(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)(ta+2); prep[tp++] = op_else; } } /* Copy/Round the blue overshoot position to the zone position. */ ta = 2; CHECK_ARGSIZE(args, ta, align->top[i].cnt*2, argsize); for (j=0; jtop[i].cnt; j++) { args[ta++] = (short)(align->top[i].blue_cvt + 1); args[ta++] = (short)(align->top[i].pos[j].cvt); } CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)CopyZone(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; if (k!=UNDEF && min_dist>0) prep[tp++] = op_eif; } /*** Align the bottom zones. */ for (i=0; ioblue_cnt/2; i++) { /* Initiate. */ min_dist = SHRT_MAX; k = UNDEF; /* switch between blues and family blues. */ if (blues->foblue_cnt) { /* Look for the closest family blue. */ k = MatchingFamily(blues->otherblues[i*2], blues->familyotherblues, blues->foblue_cnt); min_dist = ABS(blues->otherblues[i*2] - blues->familyotherblues[k]); /* Copy/Round the family overshoot position to the zone. */ if (min_dist) { cis = (short)(GetUPEM(t1m) / TransY(t1m, (funit)min_dist)); tp = (short)FamilyCutIn(prep, (USHORT)tp, cis); ta = 2; CHECK_ARGSIZE(args, ta, align->bottom[i].cnt*2, argsize); for (j=0; jbottom[i].cnt; j++) { args[ta++] = (short)(blues->familyother_cvt[k/2]); args[ta++] = (short)(align->bottom[i].pos[j].cvt); } CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)CopyZone(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; prep[tp++] = op_else; } } /* Copy/Round the blue overshoot position to the zone position. */ ta = 2; CHECK_ARGSIZE(args, ta, align->bottom[i].cnt*2, argsize); for (j=0; jbottom[i].cnt; j++) { args[ta++] = (short)(align->bottom[i].blue_cvt); args[ta++] = (short)(align->bottom[i].pos[j].cvt); } CHECK_PREPSIZE(prep, tp, ta*2+4, psize); tp = (short)CopyZone(prep, tp, args, ta); if (ta>(short)(*maxstack)) (*maxstack) = (USHORT)ta; if (k!=UNDEF && min_dist>0) prep[tp++] = op_eif; } /* EIF[] MMPEMcnt_hw, argsize); for (i=0; icnt_hw; i++) { funit width = TransY(t1m, weight->hwidths[i].width); short snap = GetSnapH(t1m, weight->hwidths[i].width); USHORT storage = weight->hwidths[i].storage; USHORT snap_ci, std_ci; short snap_cvt; std_ci = CutInSize(width, std_width, std_tres, GetUPEM(t1m)); if (snap!=UNDEF) { snap_ci = CutInSize(width, TransY(t1m, t1m->stemsnaph[snap]), std_tres, GetUPEM(t1m)); snap_cvt = (short)SNAPH_CVT(t1m, snap); ta = (short)SnapStemArgs(args, (USHORT)ta, width, STDH_CVT, (USHORT)snap_cvt, std_ci, snap_ci, storage); } else { ta = (short)StdStemArgs(args, (USHORT)ta, width, STDH_CVT, std_ci, storage); } } if (ta+2>(short)(*maxstack)) /* Args + loopcnt + fun_num */ (*maxstack) = (USHORT)(ta+2); CHECK_PREPSIZE(prep, tp, ta*2+2, psize); AssembleArgs(args, ta, prep, &tp); if (t1m->snaph_cnt) tp = (short)CreateSnapStems(prep, (USHORT)tp, (short)weight->cnt_hw); else tp = (short)CreateStdStems(prep, (USHORT)tp, (short)weight->cnt_hw); /****** * Compute width of vertical stems. */ prep[tp++] = op_svcta | SUBOP_X; if ((std_width = GetStdVW(t1m))==0) std_width = GetDefStdVW(t1m); std_width = TransX(t1m, std_width); std_tres = (USHORT)(GetUPEM(t1m) * 2 / std_width); ta = 0; CHECK_ARGSIZE(args, ta, STEMSNAPARGS*weight->cnt_vw, argsize); for (i=0; icnt_vw; i++) { funit width = TransX(t1m, weight->vwidths[i].width); short storage = (short)weight->vwidths[i].storage; short snap = GetSnapV(t1m, weight->vwidths[i].width); USHORT snap_ci, std_ci; short snap_cvt; std_ci = CutInSize(width, std_width, std_tres, GetUPEM(t1m)); if (snap!=UNDEF) { snap_ci = CutInSize(width, TransX(t1m, t1m->stemsnapv[snap]), std_tres, GetUPEM(t1m)); snap_cvt = (short)SNAPV_CVT(snap); ta = (short)SnapStemArgs(args, (USHORT)ta, width, STDV_CVT, (USHORT)snap_cvt, std_ci, snap_ci, (USHORT)storage); } else { ta = (short)StdStemArgs(args, (USHORT)ta, width, STDV_CVT, std_ci, (USHORT)storage); } } if (ta+2>(short)(*maxstack)) (*maxstack) = (USHORT)(ta+2); CHECK_PREPSIZE(prep, tp, ta*2+2, psize); AssembleArgs(args, ta, prep, &tp); if (t1m->snapv_cnt) tp = (short)CreateSnapStems(prep, (USHORT)tp, (short)weight->cnt_vw); else tp = (short)CreateStdStems(prep, (USHORT)tp, (short)weight->cnt_vw); prep[tp++] = op_rtg; /****** * Compute diagonal control parameters. */ CHECK_PREPSIZE(prep, tp, STACKINC, psize); if ((stdvw = GetStdVW(t1m))==0) stdvw = GetDefStdVW(t1m); if ((stdhw = GetStdHW(t1m))==0) stdhw = GetDefStdHW(t1m); if (stdvw && stdhw) { cis = (short)(MAX((GetUPEM(t1m) + GetUPEM(t1m)/2) / std_width, 1)); #ifdef SYMETRICAL_REDUCTION shift = (long)GetUPEM(t1m); #else shift = (long)GetUPEM(t1m)*(long)MIN(stdvw,stdhw)/ (long)MAX(stdvw, stdhw)/2L+(long)GetUPEM(t1m)/2L; #endif } else if (stdvw || stdhw) { cis = (short)(1548 / MAX(stdvw, stdhw) + 1); shift = (long)GetUPEM(t1m)/2; } else { cis = 41; shift = GetUPEM(t1m)/4; } prep[tp++] = op_pushb1; prep[tp++] = STORAGE_DIAG; prep[tp++] = op_pushb1; prep[tp++] = STDV_CVT; prep[tp++] = op_rcvt; prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)48; prep[tp++] = op_lt; prep[tp++] = op_if; #ifdef SYMETRICAL_REDUCTION /* Compute the reduction. */ shift = (short)(shift/(long)cis/4); prep[tp++] = op_npushw; prep[tp++] = 2; prep[tp++] = (UBYTE)TMPCVT; prep[tp++] = 0; prep[tp++] = HIBYTE(shift); prep[tp++] = LOBYTE(shift); prep[tp++] = op_wcvtf; prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)TMPCVT; prep[tp++] = op_rcvt; prep[tp++] = op_pushb1; prep[tp++] = MIN_REDUCTION; prep[tp++] = op_add; #else /* Compute the reduction. */ shift = (short)(shift/(long)cis/2); prep[tp++] = op_npushw; prep[tp++] = 2; prep[tp++] = (UBYTE)TMPCVT; prep[tp++] = 0; prep[tp++] = HIBYTE(shift); prep[tp++] = LOBYTE(shift); prep[tp++] = op_wcvtf; prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)TMPCVT; prep[tp++] = op_rcvt; prep[tp++] = op_pushb1; prep[tp++] = REDUCTION_C1; prep[tp++] = op_max; #endif prep[tp++] = op_else; prep[tp++] = op_pushb1; prep[tp++] = 0; prep[tp++] = op_eif; prep[tp++] = op_pushb1 + 1; prep[tp++] = VERSION_1_5; prep[tp++] = VERSION_SELECTOR; prep[tp++] = op_getinfo; prep[tp++] = op_gt; prep[tp++] = op_if; prep[tp++] = op_pushb1; prep[tp++] = 8; prep[tp++] = op_mul; prep[tp++] = op_eif; prep[tp++] = op_ws; Free(args); } (*glob_prep) = prep; return (USHORT)tp; } /*** ** Function: GetFontProg ** ** Description: ** Return the font program. ***/ const UBYTE *GetFontProg(void) { return tt_GetFontProg(); } /*** ** Function: GetFontProgSize ** ** Description: ** Return the size of the font program. ***/ const USHORT GetFontProgSize(void) { return tt_GetFontProgSize(); } /*** ** Function: GetNumFuns ** ** Description: ** Return the number of functions defined in ** the font program. ***/ const USHORT GetNumFuns(void) { return tt_GetNumFuns(); }