|  | #include <u.h> | 
|  |  | 
|  | #include <fontconfig/fontconfig.h> | 
|  | #include <ft2build.h> | 
|  | #include FT_FREETYPE_H | 
|  |  | 
|  | #include <libc.h> | 
|  | #include <draw.h> | 
|  | #include <memdraw.h> | 
|  | #include "a.h" | 
|  |  | 
|  | static FcConfig    *fc; | 
|  | static FT_Library  lib; | 
|  | static int         dpi = 96; | 
|  |  | 
|  | void | 
|  | loadfonts(void) | 
|  | { | 
|  | int i; | 
|  | FT_Error e; | 
|  | FcFontSet *sysfonts; | 
|  |  | 
|  | if(!FcInit() || (fc=FcInitLoadConfigAndFonts()) == NULL) { | 
|  | fprint(2, "fontconfig initialization failed\n"); | 
|  | exits("fontconfig failed"); | 
|  | } | 
|  |  | 
|  | e = FT_Init_FreeType(&lib); | 
|  | if(e) { | 
|  | fprint(2, "freetype initialization failed: %d\n", e); | 
|  | exits("freetype failed"); | 
|  | } | 
|  |  | 
|  | sysfonts = FcConfigGetFonts(fc, FcSetSystem); | 
|  |  | 
|  | xfont = emalloc9p(sysfonts->nfont*sizeof xfont[0]); | 
|  | memset(xfont, 0, sysfonts->nfont*sizeof xfont[0]); | 
|  | for(i=0; i<sysfonts->nfont; i++) { | 
|  | FcChar8 *fullname, *fontfile; | 
|  | int index; | 
|  | FcPattern *pat = sysfonts->fonts[i]; | 
|  |  | 
|  | if(FcPatternGetString(pat, FC_FULLNAME, 0, &fullname) != FcResultMatch || | 
|  | FcPatternGetString(pat, FC_FILE, 0, &fontfile) != FcResultMatch     || | 
|  | FcPatternGetInteger(pat, FC_INDEX, 0, &index) != FcResultMatch) | 
|  | continue; | 
|  |  | 
|  | xfont[nxfont].name     = strdup((char*)fullname); | 
|  | xfont[nxfont].fontfile = strdup((char*)fontfile); | 
|  | xfont[nxfont].index    = index; | 
|  | nxfont++; | 
|  | } | 
|  |  | 
|  | FcFontSetDestroy(sysfonts); | 
|  | } | 
|  |  | 
|  | void | 
|  | load(XFont *f) | 
|  | { | 
|  | FT_Face face; | 
|  | FT_Error e; | 
|  | FT_ULong charcode; | 
|  | FT_UInt glyph_index; | 
|  |  | 
|  | if(f->loaded) | 
|  | return; | 
|  |  | 
|  | e = FT_New_Face(lib, f->fontfile, f->index, &face); | 
|  |  | 
|  | if(e){ | 
|  | fprint(2, "load failed for %s (%s) index:%d\n", f->name, f->fontfile, f->index); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!FT_IS_SCALABLE(face)) { | 
|  | fprint(2, "%s is a non scalable font, skipping\n", f->name); | 
|  | FT_Done_Face(face); | 
|  | f->loaded = 1; | 
|  | return; | 
|  | } | 
|  |  | 
|  | f->unit = face->units_per_EM; | 
|  | f->height = (int)((face->ascender - face->descender) * 1.2); | 
|  | f->originy = face->descender; // bbox.yMin (or descender)  is negative, becase the baseline is y-coord 0 | 
|  |  | 
|  | for(charcode=FT_Get_First_Char(face, &glyph_index); glyph_index != 0; | 
|  | charcode=FT_Get_Next_Char(face, charcode, &glyph_index)) { | 
|  |  | 
|  | int idx = charcode>>8; | 
|  |  | 
|  | if(charcode > 0xffff) | 
|  | break; | 
|  |  | 
|  | if(!f->range[idx]) { | 
|  | f->range[idx] = 1; | 
|  | f->nrange++; | 
|  | } | 
|  | } | 
|  |  | 
|  | FT_Done_Face(face); | 
|  | f->loaded = 1; | 
|  | } | 
|  |  | 
|  | Memsubfont* | 
|  | mksubfont(char *name, int lo, int hi, int size, int antialias) | 
|  | { | 
|  | XFont *xf, *xfp, *xfe; | 
|  | FT_Face face; | 
|  | FT_Error e; | 
|  | Memimage *m, *mc, *m1; | 
|  | double pixel_size; | 
|  | int x, y, y0; | 
|  | int i; | 
|  | Fontchar *fc, *fc0; | 
|  | Memsubfont *sf; | 
|  | Point rect_points[4]; | 
|  |  | 
|  | xf = nil; | 
|  | for(xfp=xfont, xfe=xfont+nxfont; xfp != xfe; xfp++) { | 
|  | if(strcmp(xfp->name, name) == 0) { | 
|  | xf = xfp; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!xf) | 
|  | return nil; | 
|  |  | 
|  | e = FT_New_Face(lib, xf->fontfile, xf->index, &face); | 
|  |  | 
|  | if(e){ | 
|  | fprint(2, "load failed for %s (%s) index:%d\n", xf->name, xf->fontfile, xf->index); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | e = FT_Set_Char_Size(face, 0, size<<6, dpi, dpi); | 
|  | if(e){ | 
|  | fprint(2, "FT_Set_Char_Size failed\n"); | 
|  | FT_Done_Face(face); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | pixel_size = (dpi*size)/72.0; | 
|  | x = (int)((face->max_advance_width) * pixel_size/xf->unit + 0.99999999); | 
|  | y = (int)((face->ascender - face->descender) * pixel_size/xf->unit + 0.99999999); | 
|  | y0 = (int)(-face->descender * pixel_size/xf->unit + 0.99999999); | 
|  |  | 
|  | m = allocmemimage(Rect(0, 0, x*(hi+1-lo), y), antialias ? GREY8 : GREY1); | 
|  | if(m == nil) { | 
|  | FT_Done_Face(face); | 
|  | return nil; | 
|  | } | 
|  | mc = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); | 
|  | if(mc == nil) { | 
|  | freememimage(m); | 
|  | FT_Done_Face(face); | 
|  | return nil; | 
|  | } | 
|  | memfillcolor(m, DBlack); | 
|  | memfillcolor(mc, DBlack); | 
|  | fc = malloc((hi+2 - lo) * sizeof fc[0]); | 
|  | sf = malloc(sizeof *sf); | 
|  | if(fc == nil || sf == nil) { | 
|  | freememimage(m); | 
|  | freememimage(mc); | 
|  | free(fc); | 
|  | free(sf); | 
|  | FT_Done_Face(face); | 
|  | return nil; | 
|  | } | 
|  | fc0 = fc; | 
|  |  | 
|  | //rect_points[0] = mc->r.min; | 
|  | //rect_points[1] = Pt(mc->r.max.x, mc->r.min.y); | 
|  | //rect_points[2] = mc->r.max; | 
|  | //rect_points[3] = Pt(mc->r.min.x, mc->r.max.y); | 
|  |  | 
|  | x = 0; | 
|  | for(i=lo; i<=hi; i++, fc++) { | 
|  | int r; | 
|  | int advance; | 
|  |  | 
|  | memfillcolor(mc, DBlack); | 
|  |  | 
|  | e = FT_Load_Char(face, i, FT_LOAD_RENDER|(antialias ? 0:FT_LOAD_TARGET_MONO)); | 
|  | if(e){ | 
|  | fprint(2, "FT_Load_Char failed for %d\n", i); | 
|  | //mempoly(mc, rect_points, 4, Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | memimageline(mc, m->r.min, Pt(m->r.max.x, m->r.min.y), Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | memimageline(mc, m->r.min, Pt(m->r.min.x, m->r.max.y), Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | memimageline(mc, Pt(m->r.max.x, m->r.min.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | memimageline(mc, Pt(m->r.min.x, m->r.max.y), m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | memimageline(mc, m->r.min, m->r.max, Endsquare, Endsquare, 0, memopaque, ZP, S); | 
|  | advance = Dx(m->r); | 
|  |  | 
|  | memimagedraw(m, Rect(x, 0, x + advance, y), mc, ZP, memopaque, ZP, S); | 
|  | } else { | 
|  | FT_Bitmap *bitmap = &face->glyph->bitmap; | 
|  | uchar *base = byteaddr(mc, mc->r.min); | 
|  | advance = (face->glyph->advance.x+32) >> 6; | 
|  |  | 
|  | for(r=0; r < bitmap->rows; r++) | 
|  | memmove(base + r*mc->width*sizeof(u32int), bitmap->buffer + r*bitmap->pitch, bitmap->pitch); | 
|  |  | 
|  | memimagedraw(m, Rect(x, 0, x + advance, y), mc, | 
|  | Pt(-face->glyph->bitmap_left, -(y - y0 - face->glyph->bitmap_top)), | 
|  | memopaque, ZP, S); | 
|  | } | 
|  |  | 
|  | fc->x = x; | 
|  | fc->top = 0; | 
|  | fc->bottom = y; | 
|  | fc->left = 0; | 
|  | fc->width = advance; | 
|  | x += advance; | 
|  |  | 
|  | #ifdef DEBUG_FT_BITMAP | 
|  | for(r=0; r < bitmap->rows; r++) { | 
|  | int c; | 
|  | uchar *span = bitmap->buffer+(r*bitmap->pitch); | 
|  | for(c = 0; c < bitmap->width; c++) { | 
|  | fprint(1, "%02x", span[c]); | 
|  | } | 
|  | fprint(1,"\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifdef DEBUG_9_BITMAP | 
|  | for(r=0; r < mc->r.max.y; r++) { | 
|  | int c; | 
|  | uchar *span = base+(r*mc->width*sizeof(u32int)); | 
|  | for(c = 0; c < Dx(mc->r); c++) { | 
|  | fprint(1, "%02x", span[c]); | 
|  | } | 
|  | fprint(1,"\n"); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | fc->x = x; | 
|  |  | 
|  | // round up to 32-bit boundary | 
|  | // so that in-memory data is same | 
|  | // layout as in-file data. | 
|  | if(antialias) | 
|  | x += -x & 3; | 
|  | else | 
|  | x += -x & 31; | 
|  | m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); | 
|  | memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); | 
|  | freememimage(m); | 
|  |  | 
|  | sf->name = nil; | 
|  | sf->n = hi+1 - lo; | 
|  | sf->height = Dy(m1->r); | 
|  | sf->ascent = Dy(m1->r) - y0; | 
|  | sf->info = fc0; | 
|  | sf->bits = m1; | 
|  |  | 
|  | FT_Done_Face(face); | 
|  | return sf; | 
|  | } |