#include <ctype.h> static voidprintusage(Otfile *f){ f->print(f->aux, "usage: %s [-i GLYPH_ID | -r RUNE] [-p PPEM [-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 intdumpmap(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; gap = 1; total = 0; maxh = 0; lines = map > 1; fill = map > 2; if(n == 1){ maxh = im[0]->h; if(im[0]->baseline) maxh += im[0]->baseline; bw = 2*gap + im[0]->w; bh = 2*gap + maxh*2; npix = bw * bh; }else{ 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 = gap, 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; } 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 intprocess(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){ werrstr("draw: %r"); 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){ werrstr("draw: %r"); 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;
#include <ctype.h>
static void
printusage(Otfile *f)
{
f->print(f->aux, "usage: %s [-i GLYPH_ID | -r RUNE] [-p PPEM [-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;
gap = 1;
total = 0;
maxh = 0;
lines = map > 1;
fill = map > 2;
if(n == 1){
maxh = im[0]->h;
if(im[0]->baseline)
maxh += im[0]->baseline;
bw = 2*gap + im[0]->w;
bh = 2*gap + maxh*2;
npix = bw * bh;
}else{
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 = gap, 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;
}
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){
werrstr("draw: %r");
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){
werrstr("draw: %r");
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;