#include #include #include #include #include #include #include FT_FREETYPE_H #include "dat.h" #define DBG if(0) static FT_Library lib; static int dpi = 96; enum { Nullpic = 0x2400, // Graphic pictures for control codes }; static int ftloadmemimage(Memimage*, Rectangle, FT_Bitmap*); static void drawfcbox(Memimage*, Fontchar*, int, int); static char* fterrstr(int); static Xfont* allocxfont(void) { Xfont* font; xfont = realloc(xfont, (nxfont+1)*sizeof xfont[0]); font = &xfont[nxfont]; memset(font, 0, sizeof *font); nxfont++; return font; } void xfontinit(void) { FT_Face face; FT_Error err; Biobuf *b; Xfont *xf; char *s, *file, *line, *f[3]; int ntok; memimageinit(); if((err = FT_Init_FreeType(&lib)) != 0){ fprint(2, "freetype initialization failed: %s\n", fterrstr(err)); exits("freetype failed"); } file = "/sys/lib/fontsrv.map"; b = Bopen(file, OREAD); while((line = Brdline(b, '\n')) != nil){ line[Blinelen(b)-1] = 0; s = strchr(line, '#'); if(s != nil && (s == line || s[-1] == ' ' || s[-1] == '\t')) *s = '\0'; /* chop comment iff after whitespace */ ntok = tokenize(line, f, nelem(f)); switch(ntok){ case 1: if((err = FT_New_Face(lib, f[0], 0, &face)) != 0){ fprint(2, "%s: load failed: %s\n", f[0], fterrstr(err)); continue; } xf = allocxfont(); xf->name = smprint("%s-%s", face->family_name, face->style_name); xf->fontfile = strdup(f[0]); FT_Done_Face(face); break; case 2: xf = allocxfont(); xf->name = strdup(f[0]); xf->fontfile = strdup(f[1]); break; } } Bterm(b); } char* xfload(Xfont *f) { FT_Face face; FT_Error err; FT_ULong c; FT_UInt gi; int page; if(f->loaded) return nil; err = FT_New_Face(lib, f->fontfile, f->index, &face); if(err != 0){ fprint(2, "%s: load failed: %s\n", f->fontfile, fterrstr(err)); return fterrstr(err); } if(!FT_IS_SCALABLE(face)){ fprint(2, "%s: bitmap fonts are not supported\n", f->fontfile); FT_Done_Face(face); return "bitmap fonts are not supported"; } f->fheight = (face->ascender - face->descender) * dpi/72.0 / face->units_per_EM; f->fascent = face->ascender * dpi/72.0 / face->units_per_EM; for(c=FT_Get_First_Char(face, &gi); gi != 0; c=FT_Get_Next_Char(face, c, &gi)){ if(c > Runemax) break; page = c/PageSize; if(!f->page[page]) { f->page[page] = 1; f->npage++; } } FT_Done_Face(face); f->loaded = 1; return nil; } void xfscale(Xfont *font, int size) { font->height = size*font->fheight + 0.99999999; font->ascent = size*font->fascent + 0.99999999; } Memsubfont* mksubfont(Xfont *xf, char *name, int lo, int hi, int size, int mono) { FT_Error err; FT_Face face; FT_GlyphSlot glyph; FT_Bitmap *bits; int x, xmax, c, rune, n, height, ascent, lmode, chan; double k; Fontchar *info, *i; Memimage *m, *m1, *mc; Memsubfont *sf; USED(name); DBG fprint(2, "%s: requested range %d-%d\n", xf->fontfile, lo, hi); if((err = FT_New_Face(lib, xf->fontfile, xf->index, &face)) != 0){ werrstr("can't load: %s", fterrstr(err)); return nil; } if((err = FT_Set_Char_Size(face, 0, size<<6, dpi, dpi)) != 0){ FT_Done_Face(face); werrstr("can't scale: %s", fterrstr(err)); return nil; } chan = mono? GREY1:GREY8; n = hi-lo+1; k = dpi/72.0 * size/face->units_per_EM; height = (int)(k*(face->ascender - face->descender) + 0.99999999); ascent = (int)(k*face->ascender + 0.99999999); xmax = (int)(k*face->max_advance_width + 0.99999999); if(xmax == 0) xmax = height; DBG fprint(2, "n %d k %f xmax %d height %d ascent %d chan %d\n", n, k, xmax, height, ascent, chan); FT_Set_Pixel_Sizes(face, 0, height); // just in case m = allocmemimage(Rect(0, 0, n*xmax, height), chan); mc = allocmemimage(Rect(0, 0, 3*xmax, height), chan); info = malloc((n+1) * sizeof info[0]); if(m == nil || mc == nil || info == nil) { werrstr("mksubfont: alloc: %r"); freememimage(m); freememimage(mc); free(info); FT_Done_Face(face); return nil; } memfillcolor(m, DBlack); lmode = FT_LOAD_RENDER; if(mono) lmode |= FT_LOAD_TARGET_MONO; if(!mono && size > 9) lmode |= FT_LOAD_NO_HINTING; info[0].x = 0; for(c=0; cleft = 0; i->top = 0; i->bottom = height; i->width = 0; (i+1)->x = i->x; rune = lo+c; if((err = FT_Load_Char(face, rune, lmode)) != 0){ fprint(2, "%s rune %d: load failed: %s\n", xf->fontfile, rune, fterrstr(err)); if(rune == 0){ /* must have a valid fallback char */ drawfcbox(m, i, height, ascent); continue; }else if(rune < 32){ if(FT_Load_Char(face, rune+Nullpic, lmode) != 0) continue; }else continue; } glyph = face->glyph; bits = &face->glyph->bitmap; DBG fprint(2, "%s rune %d: loaded: %dx%d\n", xf->fontfile, rune, bits->width, bits->rows); i->left = glyph->bitmap_left; i->top = ascent - glyph->bitmap_top; i->bottom = i->top+bits->rows; if(i->bottom > height+1) i->bottom = height+1; i->width = (glyph->advance.x+(1<<5))>>6; (i+1)->x = i->x+bits->width; if(i->left < 0){ /* fix artifacts when e.g. Symbola lets "j" on top of "e" in "ej" */ i->width += -i->left; i->left = 0; } if(rune == 0 && (i->width == 0 || i->x == (i+1)->x)){ /* must have a valid fallback char */ drawfcbox(m, i, height, ascent); continue; } ftloadmemimage(mc, Rect(0, 0, bits->width, bits->rows), bits); memimagedraw(m, Rect(i->x, i->top, (i+1)->x, i->bottom), mc, mc->r.min, nil, ZP, S); } freememimage(mc); x = info[n].x; if(mono) x += -x & 31; else x += -x & 3; m1 = allocmemimage(Rect(0, 0, x, height), m->chan); if(m1 == nil){ freememimage(m); free(info); FT_Done_Face(face); return nil; } memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); freememimage(m); sf = allocmemsubfont(nil, n, height, ascent, info, m1); if(sf == nil){ freememimage(m1); free(info); FT_Done_Face(face); return nil; } FT_Done_Face(face); return sf; } static int ftloadmemimage(Memimage *i, Rectangle r, FT_Bitmap* bits) { uchar *q; int n, ndata, y, bpl; switch(bits->pixel_mode){ case FT_PIXEL_MODE_MONO: if(i->depth != 1){ werrstr("bad pixel mode %d for depth %d", bits->pixel_mode, i->depth); return -1; } break; case FT_PIXEL_MODE_GRAY: if(i->depth != 8){ werrstr("bad pixel mode %d for depth %d", bits->pixel_mode, i->depth); return -1; } break; } q = bits->buffer; bpl = bits->pitch; ndata = 0; for(y=r.min.y; y < r.max.y; y++, q += bpl){ n = loadmemimage(i, Rect(r.min.x, y, r.max.x, y+1), q, bits->width); if(n < 0) return -1; ndata += n; } return ndata; } static void drawfcbox(Memimage* m, Fontchar* i, int height, int ascent) { int w; Rectangle r; w = height - ascent; if(w < 9) w = 9; (i+1)->x = i->x+w; i->width = w+1; i->left = 1; i->top = 0; i->bottom = ascent; r = Rect(i->x, i->top, (i+1)->x, i->bottom); memimagedraw(m, r, memwhite, ZP, nil, ZP, S); memimagedraw(m, insetrect(r, 2), memblack, ZP, nil, ZP, S); } /* * get the freetype error strings - lifted from /usr/inferno/libfreetype/freetype.c */ #define FT_NOERRORDEF_(l,c,t) #define FT_ERRORDEF_(l,c,t) c,t, static struct FTerr { int code; char* text; } fterrs[] = { #include FT_ERROR_DEFINITIONS_H /* "fterrdef.h" */ -1, "", }; static char* fterrstr(int code) { int i; if(code == 0) return nil; for(i = 0; fterrs[i].code > 0; i++) { if(fterrs[i].code == code) return fterrs[i].text; } return "unknown FreeType error"; }