ref: 3582b9cc27bf2a83800c00b4a84873037e99642d
dir: /test.h/
#include <ctype.h>
static void
printusage(Otfile *f)
{
f->print(f->aux, "usage: %s [-i GLYPH_ID | -r RUNE] [-p PPEM [-g PIXELS] [-m ... -[-H GLYPH_ID]]] font.otf\n", argv0);
f->print(f->aux, " -i: operate on a single glyph by its id\n");
f->print(f->aux, " -r: operate on a single glyph by its rune\n");
f->print(f->aux, " -p: draw (of size in pixels per em) and write the image to stdout\n");
f->print(f->aux, " -m: print out glyph ids or render them all as a map (with -p)\n");
f->print(f->aux, " -R: ignore glyphs that do not represent valid runes\n");
f->print(f->aux, " -H: highlight a specific glyph in the map by its ID\n");
f->print(f->aux, " -x: horizontal ppem\n");
f->print(f->aux, " -y: vertical ppem\n");
f->print(f->aux, " -s: use subpixel rendering if applicable (rgb, bgr, rgbv, bgrv)\n");
f->print(f->aux, "Specifying -m more than once adds guide lines\n");
f->print(f->aux, "Specifying -m more than twice makes empty pixels filled out\n");
}
static u8int deffir[] = {8, 77, 86, 77, 8};
static RasterOpts ropts;
static SubPx subpx = {
.fir = {
.c = deffir,
.nc = nelem(deffir),
},
.lcdorder = LCD_ORDER_RGB,
.lcdvert = 0,
};
static int gind = -1, map, highlight = -1, runesonly;
static Rune rune = NoRune;
static char *gtypes[] = {
[GLYPH_EMPTY] = "empty",
[GLYPH_BITMAP] = "bitmap",
[GLYPH_SIMPLE] = "simple",
[GLYPH_COMPONENT] = "component",
};
static int
dumpmap(Otfile *f, GlyfImage **im, int n)
{
int x, y, i, j, t, maxh, prebase, postbase, d, border;
int gap, total, mid, npix, bw, bh, lines, fill;
u8int *b;
total = 0;
maxh = 0;
lines = map > 1;
fill = map > 2;
if(n == 1){
gap = 8;
maxh = im[0]->h;
if(im[0]->baseline)
maxh += im[0]->baseline;
bw = 2*gap + im[0]->w;
bh = 2*gap + maxh;
npix = bw * bh;
}else{
gap = 1;
for(i = 0; i < n; i++){
if(im[i] == nil)
continue;
if(maxh < im[i]->h)
maxh = im[i]->h;
total += im[i]->w;
}
mid = total / n;
npix = (mid+gap)*(maxh+gap)*n;
bh = sqrt(npix);
bw = npix/bh;
npix *= 2;
bh *= 2;
npix += bw*gap;
}
if((b = malloc(npix*3)) == nil)
return -1;
memset(b, 0xff, npix*3);
y = gap;
for(i = 0; i < n;){
prebase = postbase = 0;
for(x = 0, j = i; j < n; j++){
if(im[j] == nil)
continue;
x += im[j]->w + gap;
if(x > bw)
break;
if(prebase < im[j]->h+im[j]->baseline)
prebase = im[j]->h+im[j]->baseline;
if(im[j]->baseline < 0 && postbase < -im[j]->baseline)
postbase = -im[j]->baseline;
}
maxh = prebase + postbase;
if(j == i || y+maxh > bh)
break;
for(x = 0; i < j; i++){
if(im[i] == nil)
continue;
u8int *lt = b + ((y + prebase - (im[i]->h+im[i]->baseline))*bw + x)*3;
x += im[i]->w + gap;
for(d = 0; d < im[i]->h; d++, lt += bw*3){
for(t = 0; t < im[i]->w; t++){
u8int r, g, b;
u32int rgb;
if(im[i]->fmt == GLYF_FMT_RGB_24){
r = im[i]->b[(d*im[i]->w + t)*3 + 0];
g = im[i]->b[(d*im[i]->w + t)*3 + 1];
b = im[i]->b[(d*im[i]->w + t)*3 + 2];
}else{
r = g = b = im[i]->b[d*im[i]->w + t];
}
rgb = r<<16 | g<<8 | b;
border = d == 0 || t == 0 || t == im[i]->w-1 || d == im[i]->h-1;
if((lines || highlight == i) && rgb == 0 && (d == im[i]->h+im[i]->baseline)){
r = 0xff;
g = 0;
b = 0;
}else if((lines || highlight == i) && rgb == 0 && (fill || border)){
if(fill && !border){
r = 0xff;
g = 0x80;
b = 0xff;
}else if(border){
r = 0;
g = 0xff;
b = 0;
}
}else{
r = 0xff - r;
g = 0xff - g;
b = 0xff - b;
}
if(r != 0xff || g != 0xff || b != 0xff){
lt[t*3+0] = b;
lt[t*3+1] = g;
lt[t*3+2] = r;
}
}
}
}
y += maxh + gap;
}
f->print(f->aux, "%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, bw, y);
f->write(f->aux, b, bw*y*3);
free(b);
return 0;
}
static int
process(Otfile *in, Otfile *out)
{
GlyfImage **im;
int i, n;
Glyf *g;
Otf *o;
if((o = otfopen(in)) == nil)
return -1;
n = otfglyfnum(o);
if(rune != NoRune){
if((gind = otfrune2glyph(o, rune)) < 0){
werrstr("no such rune->glyph mapping\n");
return -1;
}
}else if(gind >= 0 && runesonly && otfglyph2rune(o, gind) == NoRune){
werrstr("no such glyph->rune mapping\n");
return -1;
}
if(gind >= n){
werrstr("out of range (max %d)", n-1);
goto glypherr;
}
if(map && gind < 0){
im = ropts.ppemX > 0 ? calloc(n, sizeof(*im)) : nil;
for(i = 0; i < n; i++){
if(runesonly && otfglyph2rune(o, i) == NoRune)
continue;
if((g = otfglyf(o, i, &ropts)) == nil){
gind = i;
glypherr:
werrstr("glyph %d: %r", gind);
return -1;
}
if(ropts.ppemX > 0 && g->type != GLYPH_EMPTY){
if((im[i] = otfdrawglyf(o, g, &ropts)) == nil){
gind = i;
goto glypherr;
}
}else if(ropts.ppemX <= 0){
out->print(out->aux, "%d (%s):\n", i, gtypes[g->type]);
print_Glyf(out, indentΔ, o, g);
}
free(g);
}
if(ropts.ppemX > 0){
if(out->write != otfdiscard && dumpmap(out, im, n) != 0)
return -1;
for(i = 0; i < n; i++)
free(im[i]);
free(im);
}
}else if(gind < 0){
otfprint(o, out, indentΔ);
}else{
if((g = otfglyf(o, gind, &ropts)) == nil){
goto glypherr;
}else if(ropts.ppemX > 0){
GlyfImage *im;
if((im = otfdrawglyf(o, g, &ropts)) == nil)
goto glypherr;
if(out->write != otfdiscard && dumpmap(out, &im, 1) != 0)
return -1;
free(im);
}else{
out->print(out->aux, "\n%d:\n", gind);
print_Glyf(out, indentΔ, o, g);
}
}
otfclose(o);
return 0;
}
#define parseoptions() \
ARGBEGIN{ \
case 'H': \
highlight = strtol(EARGF(usage(&out)), nil, 0); \
break; \
case 'i': \
if(rune != NoRune){ \
errboth: \
out.print(out.aux, "can't specify both rune and glyph\n"); \
usage(&out); \
} \
gind = strtol(EARGF(usage(&out)), nil, 0); \
break; \
case 'm': \
map++; \
break; \
case 'p': \
ropts.ppemX = ropts.ppemY = strtod(EARGF(usage(&out)), nil); \
break; \
case 'r': { \
char *s = EARGF(usage(&out)); \
if(gind >= 0) \
goto errboth; \
if(isdigit(*s) && strlen(s) > 1) \
rune = strtol(s, nil, 0); \
else if(chartorune(&rune, s) == 1 && rune == Runeerror){ \
out.print(out.aux, "invalid rune\n"); \
usage(&out); \
} \
} break; \
case 'R': \
runesonly++; \
break; \
case 's': { \
char *s = EARGF(usage(&out)); \
if(strcmp(s, "bgr") == 0) \
subpx.lcdorder = LCD_ORDER_BGR; \
else if(strcmp(s, "rgbv") == 0) \
subpx.lcdvert = 1; \
else if(strcmp(s, "bgrv") == 0){ \
subpx.lcdorder = LCD_ORDER_BGR; \
subpx.lcdvert = 1; \
}else if(strcmp(s, "rgb") != 0){ \
out.print(out.aux, "invalid subpixel configuration: %s\n", s); \
usage(&out); \
} \
ropts.subpx = &subpx; \
} break; \
case 'x': \
ropts.ppemX = strtod(EARGF(usage(&out)), nil); \
break; \
case 'y': \
ropts.ppemY = strtod(EARGF(usage(&out)), nil); \
break; \
default: \
usage(&out); \
}ARGEND \
if(argc != 1) \
usage(&out); \
if(ropts.ppemX == 0 && ropts.ppemY != 0) \
ropts.ppemX = ropts.ppemY; \
if(ropts.ppemY == 0 && ropts.ppemX != 0) \
ropts.ppemY = ropts.ppemX;