/* TTFB.C Created by Yidao CAI on JULY-1-94 Last modified on May 8, 2001 * Jan. 20, 2001: test for using instructions (& CVT, PREP), add non-TTF detection * April 28, 2000: fix a "bug" in complex glyph handling * May 14, 1999: add composite glyph support, use unicode font via code mapping * May 12, 1999: add format 4 cmap code mapping * Sept. 17, 1998: use new TTF fonts supplied by Honda Siew, and add interface for cnprint. * July 27, 1994: basic things done, but glyph part not working properly for NTU fonts, may be due to non-standard design * TTFGET.C, an experimental program to get TTF glyph data This program benefited from the TTF2PS.C (Lin YawJen & Liang Shing Ng) */ #include #include #include #include #include /*#include */ /* for CNPRINT parameter passing */ extern int mx, my; extern FILE *out, *errout; extern FILE *Rfopen(char *, char *); extern void putBitmap85(unsigned char *, int); void putBitmap85_(unsigned char *, int); extern int UnicodeTTF; /* TTF font uses unicode encoding, may need mapping */ extern void MemExit(void); short xmin, ymin; int xmax, ymax; /* gluph bounding box */ int my_xmin, my_ymin, my_xmax, my_ymax; int scl=1; /* scale if bounding box too small, < 1000 */ int LongFormat; /* indextoLocFormat */ struct TableEntry { char name[5]; unsigned long offset, len; } cmap, glyf, head, loca, maxp, name, cvt, prep; /* CVT */ short *CVT_table; int CVT_tablelen; unsigned char *PREP_table; int PREP_tablelen; int CMAP_Format; /* for CMAP format 2 */ unsigned int SubHeadKey[256]; struct SubHD { unsigned short code1, N2bytes, idOffset; short idDelta; } *SubHeader = NULL; /* for CMAP format 4 */ int CMAP4_nseg; struct SegHD { unsigned short start, end, idOffset; short idDelta; } *SegHeader = NULL; /* glyf data: treat as if they are in a struct */ int glyf_nctours, glyf_npts; /* # of contours, total points */ short *glyf_x, *glyf_y; unsigned char *glyf_flag; unsigned short *glyf_ctour_endpts; unsigned short *glyf_index_array; short point[2048], op[2048]; int pidx, oidx; /* for composite glyph */ #define ARG_1_AND_2_ARE_WORDS 0x0001 #define ARGS_ARE_XY_VALUES 0x0002 #define XY_BOUND_TO_GRID 0x0004 #define HAVE_A_SCALE 0x0008 #define MORE_COMPONENTS 0x0020 #define HAVE_XY_SCALES 0x0040 #define HAVE_A_TWO_BY_TWO 0x0080 #define HAVE_INSTRUCTIONS 0x0100 #define USE_MY_METRICS 0x0200 /* transform or translate component glyph */ short g_xoff, g_yoff; float g_xx, g_xy, g_yx, g_yy; /* maxp */ unsigned short Nglyphs, max_pts, max_ctours, max_comp_pts, max_comp_ctours, max_depth; int get_glyf(FILE *, unsigned short); unsigned short FontIndex(unsigned char, unsigned char); void load_cmap(FILE *); void load_cvt(FILE *); void load_prep(FILE *); void clear_data(); void uappend_fill85(); void PSLineto(short, short); void PSCurveto(short, short, int, int); FILE *TTFopen_init(char *); void TTFgen_PSproc(FILE *, unsigned int, unsigned int); unsigned short get2(FILE *fp) { unsigned char c1 = fgetc(fp); return ( ( (unsigned short) c1 << 8) + (unsigned char) fgetc(fp) ); } unsigned long get4(FILE *fp) { unsigned long l = get2(fp); return l*65536L + get2(fp); } int GetTable(FILE *fp) { char s[5]; int j, Ntable; int n; struct TableEntry tmp, *a; fseek(fp, 4L, 0); Ntable = get2(fp); fseek(fp, 12L, 0); n = (Ntable > 50) ? 50 : Ntable; cmap.name[0]='\0'; for (j = 0; j < n; j++) { fread(s, 1, 4, fp); s[4] = 0; if (strcmp(s, "cmap")==0) a = &cmap; else if (strcmp(s, "loca")==0) a = &loca; else if (strcmp(s, "glyf")==0) a = &glyf; else if (strcmp(s, "head")==0) a = &head; else if (strcmp(s, "maxp")==0) a = &maxp; else if (strcmp(s, "name")==0) a = &name; else if (strcmp(s, "cvt ")==0) a = &cvt; else if (strcmp(s, "prep")==0) a = &prep; else a = &tmp; strcpy(a->name, s); get4(fp); /*** skip checksum ***/ a->offset = get4(fp); a->len = get4(fp); } /* cmap is a required table. if not present, then this is not a TTF */ return (cmap.name[0]=='\0')? 0:1; } void load_prep(FILE *fp) { unsigned int j, k; fseek(fp, prep.offset, 0); PREP_tablelen = prep.len; PREP_table = (unsigned char *) calloc(sizeof(unsigned char), PREP_tablelen+1); if (PREP_table==NULL) MemExit(); for (j=0; j Nbyte1) Nbyte1 = SubHeadKey[k]; } SubHeader = (struct SubHD *) calloc(sizeof(struct SubHD), ++Nbyte1); /* or Nbyte1? */ if (SubHeader==NULL) MemExit(); for (k=0; k 0) { glyf_index_array = (unsigned short *) calloc(sizeof(unsigned short), len); if (glyf_index_array==NULL) MemExit(); for (k=0; k 0) */ for (i = 0; i < glyf_nctours; i++) glyf_ctour_endpts[i] = get2(fp); glyf_npts = glyf_ctour_endpts[glyf_nctours-1] + 1; j = get2(fp); /* # of instructions */ while (j--) getc(fp); /* ? skip instructions */ glyf_flag = (unsigned char *) calloc(sizeof(unsigned char), glyf_npts); if (glyf_flag==NULL) MemExit(); glyf_x = (short *) calloc(sizeof(short), glyf_npts); if (glyf_x==NULL) MemExit(); glyf_y = (short *) calloc(sizeof(short), glyf_npts); if (glyf_y==NULL) MemExit(); /* flags */ for (j = 0; j < glyf_npts;) { glyf_flag[j++] = c = getc(fp); if (c&8) { /* repeat */ ct = getc(fp); while (ct--) glyf_flag[j++] = c; } } /* x coordinates */ for (j = 0; j < glyf_npts; j++) { if (glyf_flag[j] & 2) { c = getc(fp); glyf_x[j] = (glyf_flag[j] & 0x10)? ((short) c) : (-1 * (short) c); } else if (glyf_flag[j] & 0x10) glyf_x[j] = 0; else glyf_x[j] = (short) get2(fp); } /* y coordinates */ for (j = 0; j < glyf_npts; j++) { if (glyf_flag[j] & 4) { c = getc(fp); glyf_y[j] = (glyf_flag[j] & 0x20)? ((short) c) : (-1 * (short) c); } else if (glyf_flag[j] & 0x20) glyf_y[j] = 0; else glyf_y[j] = (short) get2(fp); } /* x, y coordinate is always relative to previous one */ for (j = 1; j < glyf_npts; j++) { glyf_x[j] += glyf_x[j-1]; glyf_y[j] += glyf_y[j-1]; } for (j = 0; j < glyf_npts; j++) { glyf_x[j] = ((long)(glyf_x[j] - xmin)); glyf_y[j] = ((long)(glyf_y[j] - ymin)); /* res x res, 1/4 precision, ** must shift coor before scale */ /*glyf_x[j] = (((long)(glyf_x[j]-xmin))*res/(EM/4))-2; glyf_y[j] = (((long)(glyf_y[j]-ymin))*res/(EM/4))-2; */ } return 1; } short RangeCheck(short i) { if (i < 0) i = 0; return i; } void BuildGlyph() { int i, j, start_offpt, end_offpt, fst; /* first set ufill bounding box: no harm to set it bigger than needed */ point[pidx++] = 0; point[pidx++] = 0; point[pidx++] = 1248; /* xmax + 200 */ point[pidx++] = 1248; /* ymax + 200 */ op[oidx++] = 0; for (i = 0, j = 0; i < glyf_nctours; i++) { fst = j; /* PSMoveto(glyf_x[j], glyf_y[j]); */ point[pidx++] = RangeCheck(glyf_x[j]); point[pidx++] = RangeCheck(glyf_y[j]); op[oidx++] = 1; start_offpt = 0; for (j++; j <= glyf_ctour_endpts[i]; j++) { if (!(glyf_flag[j]&1)) { /*Off curve*/ if (!start_offpt) { start_offpt = end_offpt = j; } else end_offpt++; } else { /*On Curve*/ if (start_offpt) { PSCurveto(glyf_x[j], glyf_y[j], start_offpt, end_offpt); start_offpt = 0; } else PSLineto(glyf_x[j], glyf_y[j]); } } if (start_offpt) PSCurveto(glyf_x[fst], glyf_y[fst], start_offpt, end_offpt); else PSLineto(glyf_x[fst], glyf_y[fst]); } op[oidx++] = 10; /* closepath */ } void PSLineto(short x, short y) { point[pidx++] = RangeCheck(x); point[pidx++] = RangeCheck(y); op[oidx++] = 3; } void PSCurveto(short x, short y, int s, int t) { int N, i; double sx[3], sy[3], cx[4], cy[4]; N = t-s+2; for (i=0; i %s\n", ttfname); exit(0); } if (!GetTable(fpTTF)) { fprintf(errout, "%s is not a true type font file.\n", ttfname); fclose(fpTTF); return NULL; } GetMaxP(fpTTF); /* load_cvt(fpTTF); load_prep(fpTTF); */ LongFormat = GetIndexToLocFormat(fpTTF); load_cmap(fpTTF); GetBoundingBox(fpTTF); /* get glyph bounding box */ return fpTTF; } void TTFclose(FILE *fp) { fclose(fp); free(glyf_index_array); if (CMAP_Format==2) free(SubHeader); else if (CMAP_Format==4) free(SegHeader); } void uappend_fill85() /* use ASCII base-85: <~ and ~>, level 2 only */ { int i, j, count; unsigned char a[4100]; fprintf(out, "{<"); a[0] = (unsigned char) 149; a[1] = (unsigned char) 32; a[2] = (unsigned char) (pidx/256); a[3] = (unsigned char) (pidx%256); for (i=0; i\n<"); count = 1; j=0; for (i=0; i}"); } unsigned short FontIndex(unsigned char c1, unsigned char c2) /* c1, c2: high and low bytes of a HanZi char */ { unsigned short idx, ridx; unsigned short code; struct SegHD seghd; int j; if (CMAP_Format==2) { idx = SubHeadKey[c1]; /* value of Byte 1 */ if (idx==0) ridx=glyf_index_array[c1]; else { if ((short) c2 > SubHeader[idx].N2bytes + SubHeader[idx].code1) return 0; /* Error */ ridx = SubHeader[idx].idDelta; ridx += glyf_index_array[SubHeader[idx].idOffset/2 + (c2 - SubHeader[idx].code1)]; } } else if (CMAP_Format==4) { code = (unsigned short) (c1*256 + c2); for (j=0; j= CMAP4_nseg) return 0; seghd = SegHeader[j]; if (code < seghd.start) return 0; /* unlikely */ if (seghd.idOffset == 0) ridx = (code + seghd.idDelta) & 0xFFFF; else { /* ****** !!! this part may be wrong !!! ******* */ idx = seghd.idOffset / 2 + (code - seghd.start) - (CMAP4_nseg - j); if (glyf_index_array[idx] == 0 ) return 0; else ridx = (glyf_index_array[idx] + seghd.idDelta) & 0xFFFF; } } return ridx; } static float f2dot14 (short y) { return (y >> 14) + ((y & 0x3fff) / 16384.0); /*return (y << 2);*/ } void TTFgen_PSproc(FILE *fp, unsigned int c1, unsigned int c2) /* * from TTF file, generate the PS procedure for individual char * now handle composite glyph */ { unsigned short cidx = FontIndex(c1, c2); short ncontours; unsigned short flag, glyphindex, xscale, yscale, scale01, scale10; short arg1, arg2; float matrix[4]; long lcoff; int j, k; long m; unsigned short ninstr; pidx = oidx = 0; /* idx must reset after each glyf */ lcoff = glyf.offset + LocalOffset(fp, cidx); fseek(fp, lcoff, 0); glyf_nctours = (short) get2(fp); if (glyf_nctours >= 0) { g_xoff = g_yoff = 0; g_xx = g_yy = 1.0; g_xy = g_yx = 0.0; get_glyf(fp, cidx); BuildGlyph(); uappend_fill85(); clear_data(); } else if (glyf_nctours < 0) { /* composite glyph */ get2(fp); get2(fp); get2(fp); get2(fp); /* skip glyf boundingbox */ lcoff += 10L; k=0; do { g_xoff = g_yoff = 0; g_xx = g_yy = 1.0; g_xy = g_yx = 0.0; if (k++ > 0) { fprintf(out, "U\n"); pidx = oidx = 0; } fseek(fp, lcoff, 0); flag = get2(fp); glyphindex = get2(fp); lcoff += 4L; if (flag & ARG_1_AND_2_ARE_WORDS) { arg1 = (short) get2(fp); arg2 = (short) get2(fp); lcoff += 4L; } else { m = get2(fp); lcoff += 2L; arg1 = (char) (m >> 8); arg2 = (char) (m & 0xFF); } matrix[1] = matrix[2] = 0.0; if (flag & USE_MY_METRICS) { ; } if (flag & XY_BOUND_TO_GRID) { ; } if (flag & HAVE_A_SCALE) { matrix[0] = matrix[3] = f2dot14((short) get2(fp)); lcoff += 2L; } else if (flag & HAVE_XY_SCALES) { matrix[0] = f2dot14((short) get2(fp)); matrix[3] = f2dot14((short) get2(fp)); lcoff += 4L; } else if (flag & HAVE_A_TWO_BY_TWO) { matrix[0] = f2dot14((short) get2(fp)); matrix[1] = f2dot14((short) get2(fp)); matrix[2] = f2dot14((short) get2(fp)); matrix[3] = f2dot14((short) get2(fp)); lcoff += 8L; } else { matrix[0] = matrix[3] = 1.0; } g_xoff = arg1; g_yoff = arg2; g_xx = matrix[0]; g_xy = matrix[1]; g_yx = matrix[2]; g_yy = matrix[3]; get_glyf(fp, glyphindex); BuildGlyph(); uappend_fill85(); clear_data(); } while (flag & MORE_COMPONENTS); } } int get_glyf(FILE *fp, unsigned short char_idx) { short i; unsigned short j; unsigned char c, ct; long loff; float x, y; loff = LocalOffset(fp, char_idx); fseek(fp, glyf.offset + loff, 0); glyf_nctours = (short) get2(fp); if (glyf_nctours < 0) { fprintf(stderr, "glyph component is a composite glyph: exit\n"); return 0; /* composite glyph */ } /* skip glyf boundingbox */ /*get2(fp); get2(fp); get2(fp); get2(fp);*/ my_xmin=(short)get2(fp); my_ymin=(short)get2(fp); my_xmax=(short)get2(fp); my_ymax=(short)get2(fp); glyf_ctour_endpts = (unsigned short *) calloc(sizeof(unsigned short), glyf_nctours); if (glyf_ctour_endpts==NULL) MemExit(); /* if (glyf_nctours > 0) */ for (i = 0; i < glyf_nctours; i++) glyf_ctour_endpts[i] = get2(fp); glyf_npts = glyf_ctour_endpts[glyf_nctours-1] + 1; j = get2(fp); /* # of instructions */ while (j--) getc(fp); /* ? skip instructions */ glyf_flag = (unsigned char *) calloc(sizeof(unsigned char), glyf_npts); if (glyf_flag==NULL) MemExit(); glyf_x = (short *) calloc(sizeof(short), glyf_npts); if (glyf_x==NULL) MemExit(); glyf_y = (short *) calloc(sizeof(short), glyf_npts); if (glyf_y==NULL) MemExit(); /* flags */ for (j = 0; j < glyf_npts;) { glyf_flag[j++] = c = getc(fp); if (c&8) { /* repeat */ ct = getc(fp); while (ct--) glyf_flag[j++] = c; } } /* x coordinates */ for (j = 0; j < glyf_npts; j++) { if (glyf_flag[j] & 2) { c = getc(fp); glyf_x[j] = (glyf_flag[j] & 0x10)? ((short) c) : (-1 * (short) c); } else if (glyf_flag[j] & 0x10) glyf_x[j] = 0; else glyf_x[j] = (short) get2(fp); } /* y coordinates */ for (j = 0; j < glyf_npts; j++) { if (glyf_flag[j] & 4) { c = getc(fp); glyf_y[j] = (glyf_flag[j] & 0x20)? ((short) c) : (-1 * (short) c); } else if (glyf_flag[j] & 0x20) glyf_y[j] = 0; else glyf_y[j] = (short) get2(fp); } /* x, y coordinate is always relative to previous one */ for (j = 1; j < glyf_npts; j++) { glyf_x[j] += glyf_x[j-1]; glyf_y[j] += glyf_y[j-1]; } for (j = 0; j < glyf_npts; j++) { glyf_x[j] = ((long)(glyf_x[j] - xmin)*scl); glyf_y[j] = ((long)(glyf_y[j] - ymin)*scl); /* res x res, 1/4 precision, ** must shift coor before scale */ /*glyf_x[j] = (((long)(glyf_x[j]-xmin))*res/(EM/4))-2; glyf_y[j] = (((long)(glyf_y[j]-ymin))*res/(EM/4))-2; */ } /* transform by a matrix */ if (g_xx != 1.0 || g_yy != 1.0 || g_xy != 0.0 || g_yx != 0.0) { for (j = 0; j < glyf_npts; j++) { x = glyf_x[j] * g_xx + glyf_y[j] * g_yx; y = glyf_x[j] * g_xy + glyf_y[j] * g_yy; glyf_x[j] = (short) x; glyf_y[j] = (short) y; } } /* translate */ if (g_xoff || g_yoff) { g_xoff *= scl; g_yoff *= scl; for (j = 0; j < glyf_npts; j++) { glyf_x[j] += g_xoff; glyf_y[j] += g_yoff; } } return 1; } void putBitmap85_(unsigned char *a, int arraylen) { int j; for (j=0; j