#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include <ft2build.h>
#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; c<n; c++){
i = &info[c];
i->left = 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";
}
|