#include #include #include FT_FREETYPE_H #include #include #include #include FT_Library lib; //#define FLOOR(x) ((x) & -64) //#define CEIL(x) (((x) + 63) & -64) #define TRUNC(x) ((x) >> 6) //#define ROUND(x) (((x) + 32) & -64) typedef struct FTmetrics FTmetrics; typedef struct FTsubfont FTsubfont; typedef struct FTfont FTfont; typedef enum { Rmono = 1, Rantialias, Rsubpixel, } Rmode; typedef enum { Srgb = 1, Sbgr, Svrgb, Svbgr } SRgb; struct FTmetrics { int left; int right; int top; int bottom; int advance; int width; }; struct FTsubfont { int nchars; int startc; int ascent; int descent; int image_height; int image_width; FTmetrics* metrics; Fontchar* fchars; Subfont sf; Memimage* image; FTsubfont* next; FTsubfont* prev; }; struct FTfont { char* name; int size; FT_Face face; FT_GlyphSlot slot; int rmode; int rgb; int image_channels; int ascent; int descent; int height; FTsubfont* sfont; }; int f[3] = { 1, 2, 3 }; int fsum = 9; FT_Vector getScale(FTfont* font) { FT_Vector ret; ret.x = ret.y = 1; if (font->rmode == Rsubpixel) { switch (font->rgb) { case Srgb: case Sbgr: ret.x = 3; break; case Svrgb: case Svbgr: ret.y = 3; break; } } return ret; } FT_Glyph getGlyph(FTfont* font, int c) { int status; FT_Glyph glyph; FT_UInt gidx; FT_Int32 lflags; FT_Vector scale; FT_Vector pos; FT_Matrix tm; gidx = FT_Get_Char_Index(font->face, c); switch (font->rmode) { case Rmono: case Rsubpixel: default: lflags = FT_LOAD_TARGET_MONO; break; case Rantialias: lflags = FT_LOAD_DEFAULT; break; } if(status = FT_Load_Glyph(font->face, gidx, lflags)) sysfatal("FT_Load_Glyph: status=%d\n", status); if(status = FT_Get_Glyph(font->face->glyph, &glyph)) sysfatal("FT_Get_Glyph: status=%d\n", status); glyph->advance.x = font->slot->advance.x; glyph->advance.y = font->slot->advance.y; scale = getScale(font); tm.xx = 0x10000 * scale.x; tm.yy = 0x10000 * scale.y; tm.xy = tm.yx = 0; pos.x = pos.y = 0; if(status = FT_Glyph_Transform(glyph, &tm, &pos)) sysfatal("FT_Glyph_Transform: status=%d\n", status); return glyph; } FT_BitmapGlyph getBitmapGlyph(FTfont* font, FT_Glyph glyph) { int rmode = 0; int status; switch (font->rmode) { case Rmono: rmode = FT_RENDER_MODE_MONO; break; case Rantialias: case Rsubpixel: rmode = FT_RENDER_MODE_NORMAL; break; default: sysfatal("error: rmode != antialias|mono|subpixel"); } if(status = FT_Glyph_To_Bitmap(&glyph, rmode, 0, 1)) sysfatal("FT_Glyph_To_Bitmap: status=%d\n", status); return (FT_BitmapGlyph) glyph; } FTmetrics getMetrics(FTfont* font, int c) { FT_Glyph glyph; FT_BitmapGlyph bglyph; FT_Bitmap bitmap; FT_Vector scale; FTmetrics ret; glyph = getGlyph(font, c); bglyph = getBitmapGlyph(font, glyph); scale = getScale(font); bitmap = bglyph->bitmap; ret.left = bglyph->left / scale.x; ret.right = (bglyph->left + bitmap.width) / scale.x; ret.top = bglyph->top / scale.y; ret.bottom = (bglyph->top - bitmap.rows) / scale.y; ret.width = bitmap.width / scale.x; ret.advance = TRUNC(font->slot->advance.x); /* usage of slot ??? */ if (font->rmode == Rsubpixel) { switch (font->rgb) { case Srgb: case Sbgr: ret.left -= 1; ret.right += 1; ret.width += 2; break; case Svrgb: case Svbgr: ret.top -= 1; ret.bottom -= 1; break; } } // if(ret.left < 0) // ret.left = 0; // FT_Done_Glyph(glyph); return ret; } ulong getPixelMono(FTfont* font, FT_Bitmap* bitmap, int x, int y) { ulong u; u = ((bitmap->buffer[bitmap->pitch*y + x/8]) >> (7 - x%8) & 1) * 255; return u << 24 | u << 16 | u << 8 | 0xFF; } ulong getPixelAntialias(FTfont* font, FT_Bitmap* bitmap, int x, int y) { ulong u; u = bitmap->buffer[bitmap->pitch*y + x]; return u << 24 | u << 16 | u << 8 | 0xFF; } ulong getPixelSubpixel(FTfont* font, FT_Bitmap* bitmap, int x, int y) { int h[7], xc, yc, xs, ys, xx, yy, a, b, c, i; FT_Vector scale; if(font->rgb == Srgb || font->rgb == Sbgr){ xs = 1; ys = 0; }else{ xs = 0; ys = 1; } scale = getScale(font); xx = x * scale.x; yy = y * scale.y; for(i = -2; i < 5; i++) { xc = xx + (i - scale.x) * xs; yc = yy + (i - scale.y) * ys; if (xc<0 || xc >= bitmap->width || yc<0 || yc>=bitmap->rows) h[i + 2] = 0; else h[i + 2] = bitmap->buffer[yc * bitmap->pitch + xc] * 65536; } a = (h[0]*f[0] + h[1]*f[1] + h[2]*f[2] + h[3]*f[1] + h[4]*f[0]) / fsum; b = (h[1]*f[0] + h[2]*f[1] + h[3]*f[2] + h[4]*f[1] + h[5]*f[0]) / fsum; c = (h[2]*f[0] + h[3]*f[1] + h[4]*f[2] + h[5]*f[1] + h[6]*f[0]) / fsum; if(font->rgb == Srgb || font->rgb == Svrgb) return ((a / 65536) << 24) | ((b / 65536) << 16) | ((c / 65536) << 8) | 0xFF; return ((a / 65536) << 8) | ((b / 65536) << 16) | ((c / 65536) << 24) | 0xFF; } typedef ulong (*getpixelfunc)(FTfont*, FT_Bitmap*, int, int); getpixelfunc getPixelFunc(FTfont* font) { switch (font->rmode) { case Rmono: return getPixelMono; case Rantialias: return getPixelAntialias; case Rsubpixel: return getPixelSubpixel; } sysfatal("getpixelfunc: bad rmode"); return nil; } void drawChar(FTfont* font, Memimage* img, int x0, int y0, int c) { int height, width, x, y; unsigned long cl; Point pt; Rectangle r; Memimage* color; FT_Glyph glyph; FT_BitmapGlyph bglyph; FT_Bitmap bitmap; FT_Vector scale; getpixelfunc getPixel; glyph = getGlyph(font, c); scale = getScale(font); bglyph = getBitmapGlyph(font, glyph); bitmap = bglyph->bitmap; getPixel = getPixelFunc(font); r.min.x = 0; r.min.y = 0; r.max.x = 1; r.max.y = 1; color = allocmemimage(r, font->image_channels); r.min.x = r.min.y = 0; r.max.x = bitmap.width; r.max.y = bitmap.rows; width = bitmap.width / scale.x; height = bitmap.rows / scale.y; if (font->rmode == Rsubpixel) { switch (font->rgb) { case Srgb: case Sbgr: width += 2; break; case Svrgb: case Svbgr: height += 2; break; } } for(y = 0; y < height; y++) { for(x = 0; x < width; x++) { cl = getPixel(font, &bitmap, x, y); pt.x = x + x0; pt.y = y + y0; memfillcolor(color, cl); memimagedraw(img, rectaddpt(Rect(0, 0, 1, 1), pt), color, ZP, nil, ZP, SatopD); } } freememimage(color); } void initSubfont(FTfont* font, FTsubfont* sf, int startc) { int i; int width; int ascent, descent; FTmetrics rc[257]; /* get first defined character */ if (startc != 0) for(; startc<=Runemax; startc++) if(FT_Get_Char_Index(font->face, startc) != 0) break; ascent = 0; descent = 100000; width = 0; for(i = 0; i < nelem(rc); i++) { if (startc+i > Runemax || width>2000) break; if (i > 0 && FT_Get_Char_Index(font->face, startc + i) == 0) { rc[i] = getMetrics(font, 0); rc[i].width = 0; rc[i].advance = 0; } else rc[i] = getMetrics(font, startc + i); if (ascent < rc[i].top) ascent = rc[i].top; if (descent > rc[i].bottom) descent = rc[i].bottom; width += rc[i].width; // +1? } sf->startc = startc; sf->nchars = i; sf->ascent = ascent; sf->descent = descent; sf->image_width = width; sf->metrics = malloc(i * sizeof(FTmetrics)); sf->fchars = malloc((i + 1) * sizeof(Fontchar)); for(i = 0; i < sf->nchars; i++) sf->metrics[i] = rc[i]; } int createSubfont(FTfont* font, FTsubfont* sf, char* fname) { int i, x, fd; Rectangle r; sf->image_height = sf->ascent - sf->descent; // fprint(2, "creating %d: (%d, %d)\n", sf->startc, sf->image_height, sf->image_width); r = Rect(0, 0, sf->image_width, font->height /*sf->image_height*/); if (sf->image_width <= 0 || sf->image_height <= 0) return 1; sf->image = allocmemimage(r, font->image_channels); memfillcolor(sf->image, 0x000000FF); x = 0; for(i = 0; i < sf->nchars; i++) { sf->fchars[i].x = x; sf->fchars[i].width = sf->metrics[i].advance; sf->fchars[i].left = sf->metrics[i].left; if(sf->fchars[i].left<0)fprint(2, "LEFT < 0 YO YO\n"); sf->fchars[i].bottom = /*sf->ascent*/ font->ascent - sf->metrics[i].bottom; sf->fchars[i].top = (font->ascent - sf->metrics[i].top) > 0 ? font->ascent - sf->metrics[i].top : 0; x += sf->metrics[i].width; // +1? drawChar(font, sf->image, sf->fchars[i].x, sf->fchars[i].top, i + sf->startc); } sf->fchars[i].x = x; sf->sf.name = font->name; sf->sf.n = sf->nchars; sf->sf.height = font->height; /*sf->image_height; */ sf->sf.ascent = font->ascent /*sf->ascent */; sf->sf.info = sf->fchars; sf->sf.ref = 1; fd = create(fname, OWRITE, 0666); if(fd < 0) sysfatal("can not create font file: %r"); writememimage(fd, sf->image); writesubfont(fd, &sf->sf); close(fd); freememimage(sf->image); sf->image = nil; return 0; } void usage(void) { fprint(2, "\n" "usage: ttf2subf \n" " -s size - point size\n" " -h - this message\n" " -f fname - ttf file name\n" " -n name - font name\n" " -m mono|antialias|subpixel - rendering mode\n" " -r rgb|bgr|vrgb|vbgr - LCD display type\n\n"); exits("usage"); } static struct { Rmode m; char* s; } tab[] = { {Rmono, "mono"}, {Rantialias, "antialias"}, {Rsubpixel, "subpixel"}, }; static Rmode strtomode(char *s) { int i; for(i = 0; i < nelem(tab); i++) if (strcmp(s, tab[i].s) == 0) return tab[i].m; sysfatal("unknown mode"); return tab[0].m; } static struct { Rmode m; char* s; } tab2[] = { {Srgb, "rgb"}, {Sbgr, "bgr"}, {Svrgb, "vrgb"}, {Svbgr, "vbgr"}, }; static SRgb strtorgb(char *s) { int i; for(i = 0; i < nelem(tab2); i++) if (strcmp(s, tab2[i].s) == 0) return tab2[i].m; sysfatal("unknown rgb type"); return tab2[i].m; } void main(int argc, char* argv[]) { char *fname, buf[256]; int status, i, fd; FTfont font; FTsubfont* sf; SRgb srgb; memset(&font, 0, sizeof font); fname = 0; srgb = 0; if(argc == 1) usage(); ARGBEGIN { case 's': font.size = atoi(EARGF(usage())); break; case 'h': usage(); break; /* never reached */ case 'f': fname = EARGF(usage()); break; case 'n': font.name = EARGF(usage()); break; case 'm': font.rmode = strtomode(EARGF(usage())); break; case 'r': srgb = strtorgb(EARGF(usage())); break; default: usage(); } ARGEND if (fname == nil) sysfatal("no fount name specified"); if (font.size == 0) sysfatal("no font size specified"); if (font.name == nil) font.name = fname; switch (font.rmode) { case Rmono: font.image_channels = GREY1; break; case Rantialias: font.image_channels = GREY8; break; case Rsubpixel: font.image_channels = RGB24; font.rgb = srgb; break; } print("font file: %s, font name: %s, size: %d\n", fname, font.name, font.size); memimageinit(); if((status = FT_Init_FreeType(&lib)) != 0) sysfatal("FT_Init_FreeType: status=%d\n", status); if(FT_New_Face(lib, fname, 0, &font.face) != 0) sysfatal("FT_New_Face: status=%d\n", status); // if(FT_Set_Char_Size(font.face, 0, font.size * 64, 72, 72) != 0) if(FT_Set_Char_Size(font.face, 0, font.size * 64, 72, 72) != 0) sysfatal("FT_Set_Char_Size: status=%d\n", status); FT_Select_Charmap(font.face, ft_encoding_unicode); font.slot = font.face->glyph; font.sfont = nil; font.ascent = 0; font.descent = 10000; for(i =0; i<=Runemax; ){ FTsubfont* sf = malloc(sizeof *sf); initSubfont(&font, sf, i); if (sf->nchars == 0) break; print("last: %d, start: %d, nchars: %d, width: %d\n", i, sf->startc, sf->nchars, sf->image_width); i = sf->startc + sf->nchars; sf->next = font.sfont; font.sfont = sf; if (font.ascent < sf->ascent) font.ascent = sf->ascent; if (font.descent > sf->descent) font.descent = sf->descent; } /* font.height = font.ascent - font.descent; */ font.height = font.size; font.ascent = font.height-3; /* do we have a directory created for this font? */ snprint(buf, sizeof(buf), "%s", font.name); if(access(buf, AEXIST) == -1) { fd = create(buf, OREAD, DMDIR|0777); if(fd < 0) sysfatal("cannot create font directory: %r"); } snprint(buf, sizeof(buf), "%s/%s.%d.font", font.name, font.name, font.size); fd = create(buf, OWRITE, 0666); if(fd < 0) sysfatal("cannot create .font file: %r"); fprint(fd, "%d\t%d\n", font.height, font.ascent); for(sf = font.sfont; sf != nil; sf = sf->next) { snprint(buf, sizeof buf, "%s/%s.%d.%04x", font.name, font.name, font.size, sf->startc); // fprint("generating 0x%04x - 0x%04x\n", sf->startc, sf->startc + sf->nchars); if (createSubfont(&font, sf, buf) == 0) { snprint(buf, sizeof(buf), "%s.%d.%04x", font.name, font.size, sf->startc); fprint(fd, "0x%04x\t0x%04x\t%s\n", sf->startc, sf->startc+sf->nchars - 1, buf); } } close(fd); }