#include <u.h>#include <libc.h>#include <bio.h>#include <ttf.h>#include "impl.h" static intttfparsekern(TTFontU *f){ u16int ver, len, cov, ntab; int i; if(ttfgototable(f, "kern") < 0) return -1; ttfunpack(f, "ww", &ver, &ntab); if(ver != 0) return -1; if(ntab == 0) return -1; for(i = 0; i < ntab; i++){ ttfunpack(f, "www", &ver, &len, &cov); if((cov & 1) != 0) break; Bseek(f->bin, len - 6, 1); } ttfunpack(f, "w6", &len); f->nkern = len; f->kern = mallocz(sizeof(TTKern) * len, 1); for(i = 0; i < len; i++) ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val); return 0;} static intttfkern(TTFont *f, int l, int r){ u32int idx; int a, b, c; TTFontU *u; u = f->u; if(u->nkern == 0) return 0; idx = l << 16 | r; a = 0; b = u->nkern - 1; if(u->kern[a].idx > idx || u->kern[b].idx < idx) return 0; while(a <= b){ c = (a + b) / 2; if(u->kern[c].idx < idx){ a = c + 1; }else if(u->kern[c].idx > idx){ b = c - 1; }else return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize); } return 0;} typedef struct { TTBitmap *b; TTFont *font; TTGlyph **glyph; int *gwidth; char **cpos; char *pp; int nglyph, aglyph; int linew; int adj; int nspc; int oy, lh; int spcidx; int flags; int spcw; int spcminus;} Render; static intaddglyph(Render *r, char *p, TTGlyph *g){ void *v; int k; if(r->nglyph >= r->aglyph){ r->aglyph += 32; v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph); if(v == nil) return -1; r->glyph = v; v = realloc(r->gwidth, sizeof(int) * r->aglyph); if(v == nil) return -1; r->gwidth = v; v = realloc(r->cpos, sizeof(char *) * r->aglyph); if(v == nil) return -1; r->cpos = v; } r->glyph[r->nglyph] = g; r->cpos[r->nglyph] = p; r->gwidth[r->nglyph] = g->advanceWidthpx; if(r->nglyph > 0){ k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx); r->gwidth[r->nglyph-1] += k; r->linew += k; } r->nglyph++; r->linew += r->gwidth[r->nglyph-1]; if(g->idx == r->spcidx) r->nspc++; return 0;} static voidflushglyphs(Render *r, int justify){ int i, n, k, x, y; int llen; int adj, spcw, nspc, c; TTFont *f; f = r->font; if((r->flags & TTFMODE) == TTFLALIGN && !justify) while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){ r->linew -= r->gwidth[--r->nglyph]; r->nspc--; } llen = r->linew; k = n = r->nglyph; nspc = r->nspc; adj = (nspc * r->spcminus + 63) / 64; if(r->linew - adj > r->b->width){ n = r->nglyph; while(n > 0 && r->glyph[n - 1]->idx != r->spcidx) llen -= r->gwidth[--n]; k = n; while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){ llen -= r->gwidth[--n]; nspc--; } if(n == 0){ while(n < r->nglyph && llen + r->gwidth[n] < r->b->width) llen += r->gwidth[n++]; k = n; } } if(justify){ if(nspc == 0) spcw = 0; else spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc; }else spcw = r->spcw * 64; switch(r->flags & TTFMODE | justify * TTFJUSTIFY){ case TTFRALIGN: x = r->b->width - llen; break; case TTFCENTER: x = (r->b->width - llen)/2; break; default: x = 0; } y = r->oy + f->ascentpx; c = 0; for(i = 0; i < k; i++){ if(r->glyph[i]->idx == r->spcidx){ c += spcw; x += c >> 6; c &= 63; r->nspc--; }else{ ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height); x += r->gwidth[i]; } r->linew -= r->gwidth[i]; ttfputglyph(r->glyph[i]); } if(n > 0) r->pp = r->cpos[n-1]; r->oy += r->lh; memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *)); memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *)); memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int)); r->nglyph -= k;} TTBitmap *_ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp){ Render r; Rune ch; int i, adj; TTGlyph *g; if(rp != nil) *rp = p; if(f->u->nkern < 0 && ttfparsekern(f->u) < 0) f->u->nkern = 0; memset(&r, 0, sizeof(Render)); r.flags = flags; r.font = f; r.b = ttfnewbitmap(w, h); if(r.b == nil) goto error; r.oy = 0; r.lh = f->ascentpx + f->descentpx; r.pp = p; g = ttfgetglyph(f, ttffindchar(f, ' '), 1); r.spcidx = g->idx; r.spcw = g->advanceWidthpx; if((flags & TTFJUSTIFY) != 0) r.spcminus = r.spcw * 21; else r.spcminus = 0; while(p < end && r.oy + r.lh < h){ p += getrune(&ch, p); if(ch == '\n'){ flushglyphs(&r, 0); continue; } g = ttfgetglyph(f, ttffindchar(f, ch), 1); if(g == nil){ g = ttfgetglyph(f, 0, 1); if(g == nil) continue; } if(addglyph(&r, p, g) < 0) goto error; adj = (r.nspc * r.spcminus + 63) / 64; if(r.linew - adj > r.b->width){ flushglyphs(&r, (flags & TTFJUSTIFY) != 0); } } if(r.oy + r.lh < h) flushglyphs(&r, 0); for(i = 0; i < r.nglyph; i++) ttfputglyph(r.glyph[i]); free(r.glyph); free(r.gwidth); free(r.cpos); if(rp != nil) *rp = r.pp; return r.b;error: ttffreebitmap(r.b); free(r.glyph); free(r.gwidth); return nil;} TTBitmap *ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr){ if(str == nil) end = nil; else if(end == nil) end = str + strlen(str); return _ttfrender(f, chartorune, str, end, w, h, flags, rstr);} static intincrune(Rune *r, char *s){ *r = *(Rune*)s; return sizeof(Rune);} TTBitmap *ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr){ if(str == nil) end = nil; else if(end == nil) end = str + runestrlen(str); return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr);}
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ttf.h>
#include "impl.h"
static int
ttfparsekern(TTFontU *f)
{
u16int ver, len, cov, ntab;
int i;
if(ttfgototable(f, "kern") < 0)
return -1;
ttfunpack(f, "ww", &ver, &ntab);
if(ver != 0)
return -1;
if(ntab == 0)
return -1;
for(i = 0; i < ntab; i++){
ttfunpack(f, "www", &ver, &len, &cov);
if((cov & 1) != 0) break;
Bseek(f->bin, len - 6, 1);
}
ttfunpack(f, "w6", &len);
f->nkern = len;
f->kern = mallocz(sizeof(TTKern) * len, 1);
for(i = 0; i < len; i++)
ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val);
return 0;
}
static int
ttfkern(TTFont *f, int l, int r)
{
u32int idx;
int a, b, c;
TTFontU *u;
u = f->u;
if(u->nkern == 0)
return 0;
idx = l << 16 | r;
a = 0;
b = u->nkern - 1;
if(u->kern[a].idx > idx || u->kern[b].idx < idx)
return 0;
while(a <= b){
c = (a + b) / 2;
if(u->kern[c].idx < idx){
a = c + 1;
}else if(u->kern[c].idx > idx){
b = c - 1;
}else
return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize);
}
return 0;
}
typedef struct {
TTBitmap *b;
TTFont *font;
TTGlyph **glyph;
int *gwidth;
char **cpos;
char *pp;
int nglyph, aglyph;
int linew;
int adj;
int nspc;
int oy, lh;
int spcidx;
int flags;
int spcw;
int spcminus;
} Render;
static int
addglyph(Render *r, char *p, TTGlyph *g)
{
void *v;
int k;
if(r->nglyph >= r->aglyph){
r->aglyph += 32;
v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph);
if(v == nil) return -1;
r->glyph = v;
v = realloc(r->gwidth, sizeof(int) * r->aglyph);
if(v == nil) return -1;
r->gwidth = v;
v = realloc(r->cpos, sizeof(char *) * r->aglyph);
if(v == nil) return -1;
r->cpos = v;
}
r->glyph[r->nglyph] = g;
r->cpos[r->nglyph] = p;
r->gwidth[r->nglyph] = g->advanceWidthpx;
if(r->nglyph > 0){
k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx);
r->gwidth[r->nglyph-1] += k;
r->linew += k;
}
r->nglyph++;
r->linew += r->gwidth[r->nglyph-1];
if(g->idx == r->spcidx)
r->nspc++;
return 0;
}
static void
flushglyphs(Render *r, int justify)
{
int i, n, k, x, y;
int llen;
int adj, spcw, nspc, c;
TTFont *f;
f = r->font;
if((r->flags & TTFMODE) == TTFLALIGN && !justify)
while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){
r->linew -= r->gwidth[--r->nglyph];
r->nspc--;
}
llen = r->linew;
k = n = r->nglyph;
nspc = r->nspc;
adj = (nspc * r->spcminus + 63) / 64;
if(r->linew - adj > r->b->width){
n = r->nglyph;
while(n > 0 && r->glyph[n - 1]->idx != r->spcidx)
llen -= r->gwidth[--n];
k = n;
while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){
llen -= r->gwidth[--n];
nspc--;
}
if(n == 0){
while(n < r->nglyph && llen + r->gwidth[n] < r->b->width)
llen += r->gwidth[n++];
k = n;
}
}
if(justify){
if(nspc == 0)
spcw = 0;
else
spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc;
}else
spcw = r->spcw * 64;
switch(r->flags & TTFMODE | justify * TTFJUSTIFY){
case TTFRALIGN:
x = r->b->width - llen;
break;
case TTFCENTER:
x = (r->b->width - llen)/2;
break;
default:
x = 0;
}
y = r->oy + f->ascentpx;
c = 0;
for(i = 0; i < k; i++){
if(r->glyph[i]->idx == r->spcidx){
c += spcw;
x += c >> 6;
c &= 63;
r->nspc--;
}else{
ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height);
x += r->gwidth[i];
}
r->linew -= r->gwidth[i];
ttfputglyph(r->glyph[i]);
}
if(n > 0)
r->pp = r->cpos[n-1];
r->oy += r->lh;
memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *));
memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *));
memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int));
r->nglyph -= k;
}
TTBitmap *
_ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp)
{
Render r;
Rune ch;
int i, adj;
TTGlyph *g;
if(rp != nil) *rp = p;
if(f->u->nkern < 0 && ttfparsekern(f->u) < 0)
f->u->nkern = 0;
memset(&r, 0, sizeof(Render));
r.flags = flags;
r.font = f;
r.b = ttfnewbitmap(w, h);
if(r.b == nil) goto error;
r.oy = 0;
r.lh = f->ascentpx + f->descentpx;
r.pp = p;
g = ttfgetglyph(f, ttffindchar(f, ' '), 1);
r.spcidx = g->idx;
r.spcw = g->advanceWidthpx;
if((flags & TTFJUSTIFY) != 0)
r.spcminus = r.spcw * 21;
else
r.spcminus = 0;
while(p < end && r.oy + r.lh < h){
p += getrune(&ch, p);
if(ch == '\n'){
flushglyphs(&r, 0);
continue;
}
g = ttfgetglyph(f, ttffindchar(f, ch), 1);
if(g == nil){
g = ttfgetglyph(f, 0, 1);
if(g == nil)
continue;
}
if(addglyph(&r, p, g) < 0)
goto error;
adj = (r.nspc * r.spcminus + 63) / 64;
if(r.linew - adj > r.b->width){
flushglyphs(&r, (flags & TTFJUSTIFY) != 0);
}
}
if(r.oy + r.lh < h)
flushglyphs(&r, 0);
for(i = 0; i < r.nglyph; i++)
ttfputglyph(r.glyph[i]);
free(r.glyph);
free(r.gwidth);
free(r.cpos);
if(rp != nil)
*rp = r.pp;
return r.b;
error:
ttffreebitmap(r.b);
free(r.glyph);
free(r.gwidth);
return nil;
}
TTBitmap *
ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr)
{
if(str == nil)
end = nil;
else if(end == nil)
end = str + strlen(str);
return _ttfrender(f, chartorune, str, end, w, h, flags, rstr);
}
static int
incrune(Rune *r, char *s)
{
*r = *(Rune*)s;
return sizeof(Rune);
}
TTBitmap *
ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr)
{
if(str == nil)
end = nil;
else if(end == nil)
end = str + runestrlen(str);
return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr);
}