Plan 9 from Bell Labs’s /usr/web/sources/contrib/quanstro/ttf2subf/main.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftglyph.h>

#include <libc.h>
#include <draw.h>
#include <memdraw.h>

FT_Library lib;

//#define FLOOR(x) ((x) & -64)
//#define CEIL(x) (((x) + 63) & -64)
#define TRUNC(x) ((x) >> 6)
//#define ROUND(x) (((x) + 32) & -64)

typedef	struct	FTmetrics	FTmetrics;
typedef	struct	FTsubfont	FTsubfont;
typedef	struct	FTfont		FTfont;

typedef enum {
	Rmono = 1,
	Rantialias,
	Rsubpixel,
} Rmode;

typedef enum {
	Srgb = 1,
	Sbgr,
	Svrgb,
	Svbgr
} SRgb;

struct FTmetrics {
	int left;
	int right;
	int top;
	int bottom;
	int advance;
	int width;
};

struct FTsubfont {
	int nchars;
	int startc;
	int ascent;
	int descent;
	int image_height;
	int image_width;
	FTmetrics* metrics;
	Fontchar* fchars;
	Subfont sf;
	Memimage* image;
	FTsubfont* next;
	FTsubfont* prev;
};

struct FTfont {
	char* name;
	int size;
	FT_Face face;
	FT_GlyphSlot slot;
	int rmode;
	int rgb;
	int image_channels;
	int ascent;
	int descent;
	int height;
	FTsubfont* sfont;
};

int f[3] = { 1, 2, 3 };
int fsum = 9;

FT_Vector getScale(FTfont* font) {
	FT_Vector ret;

	ret.x = ret.y = 1;
	if (font->rmode == Rsubpixel) {
		switch (font->rgb) {
		case Srgb:
		case Sbgr:
			ret.x = 3;
			break;

		case Svrgb:
		case Svbgr:
			ret.y = 3;
			break;
		}
	}

	return ret;
}

FT_Glyph
getGlyph(FTfont* font, int c)
{
	int status;
	FT_Glyph glyph;
	FT_UInt gidx;
	FT_Int32 lflags;
	FT_Vector scale;
	FT_Vector pos;
	FT_Matrix tm;

	gidx = FT_Get_Char_Index(font->face, c);

	switch (font->rmode) {
	case Rmono:
	case Rsubpixel:
	default:
		lflags = FT_LOAD_TARGET_MONO;
		break;
	case Rantialias:
		lflags = FT_LOAD_DEFAULT;
		break;
	}

	if(status = FT_Load_Glyph(font->face, gidx, lflags))
		sysfatal("FT_Load_Glyph: status=%d\n", status);

	if(status = FT_Get_Glyph(font->face->glyph, &glyph))
		sysfatal("FT_Get_Glyph: status=%d\n", status);

	glyph->advance.x = font->slot->advance.x;
	glyph->advance.y = font->slot->advance.y;

	scale = getScale(font);
	tm.xx = 0x10000 * scale.x;
	tm.yy = 0x10000 * scale.y;
	tm.xy = tm.yx = 0;
	pos.x = pos.y = 0;

	if(status = FT_Glyph_Transform(glyph, &tm, &pos))
		sysfatal("FT_Glyph_Transform: status=%d\n", status);

	return glyph;
}

FT_BitmapGlyph
getBitmapGlyph(FTfont* font, FT_Glyph glyph)
{
	int rmode = 0;
	int status;

	switch (font->rmode) {
	case Rmono:
		rmode = FT_RENDER_MODE_MONO;
		break;
	case Rantialias:
	case Rsubpixel:
		rmode = FT_RENDER_MODE_NORMAL;
		break;
	default:
		sysfatal("error: rmode != antialias|mono|subpixel");
	}

	if(status = FT_Glyph_To_Bitmap(&glyph, rmode, 0, 1))
		sysfatal("FT_Glyph_To_Bitmap: status=%d\n", status);

	return (FT_BitmapGlyph) glyph;
}

FTmetrics
getMetrics(FTfont* font, int c)
{
	FT_Glyph glyph;
	FT_BitmapGlyph bglyph;
	FT_Bitmap bitmap;
	FT_Vector scale;
	FTmetrics ret;

	glyph = getGlyph(font, c);
	bglyph = getBitmapGlyph(font, glyph);
	scale = getScale(font);
	bitmap = bglyph->bitmap;

	ret.left =  bglyph->left / scale.x;
	ret.right = (bglyph->left + bitmap.width) / scale.x;
	ret.top = bglyph->top / scale.y;
	ret.bottom = (bglyph->top - bitmap.rows) / scale.y;

	ret.width = bitmap.width / scale.x;
	ret.advance = TRUNC(font->slot->advance.x); /* usage of slot ??? */

	if (font->rmode == Rsubpixel) {
		switch (font->rgb) {
		case Srgb:
		case Sbgr:
			ret.left -= 1;
			ret.right += 1;
			ret.width += 2;
			break;
		case Svrgb:
		case Svbgr:
			ret.top -= 1;
			ret.bottom -= 1;
			break;
		}
	}

//	if(ret.left < 0)
//		ret.left = 0;

//	FT_Done_Glyph(glyph);

	return ret;
}

ulong
getPixelMono(FTfont* font, FT_Bitmap* bitmap, int x, int y)
{
	ulong u;

	u = ((bitmap->buffer[bitmap->pitch*y + x/8]) >> (7 - x%8) & 1) * 255;
	return u << 24 | u << 16 | u << 8 | 0xFF;
}

ulong
getPixelAntialias(FTfont* font, FT_Bitmap* bitmap, int x, int y)
{
	ulong u;

	u = bitmap->buffer[bitmap->pitch*y + x];
	return u << 24 | u << 16 | u << 8 | 0xFF;
}

ulong
getPixelSubpixel(FTfont* font, FT_Bitmap* bitmap, int x, int y)
{
	int h[7], xc, yc, xs, ys, xx, yy, a, b, c, i;
	FT_Vector scale;

	if(font->rgb == Srgb || font->rgb == Sbgr){
		xs = 1;
		ys = 0;
	}else{
		xs = 0;
		ys = 1;
	}
	scale = getScale(font);
	xx = x * scale.x;
	yy = y * scale.y;

	for(i = -2; i < 5; i++) {
		xc = xx + (i - scale.x) * xs;
		yc = yy + (i - scale.y) * ys;

		if (xc<0 || xc >= bitmap->width || yc<0 || yc>=bitmap->rows)
			h[i + 2] = 0;
		else
			h[i + 2] = bitmap->buffer[yc * bitmap->pitch + xc] * 65536;
	}

	a = (h[0]*f[0] + h[1]*f[1] + h[2]*f[2] + h[3]*f[1] + h[4]*f[0]) / fsum;
	b = (h[1]*f[0] + h[2]*f[1] + h[3]*f[2] + h[4]*f[1] + h[5]*f[0]) / fsum;
	c = (h[2]*f[0] + h[3]*f[1] + h[4]*f[2] + h[5]*f[1] + h[6]*f[0]) / fsum;

	if(font->rgb == Srgb || font->rgb == Svrgb)
		return ((a / 65536) << 24) | ((b / 65536) << 16) | ((c / 65536) << 8) | 0xFF;
	return ((a / 65536) << 8) | ((b / 65536) << 16) | ((c / 65536) << 24) | 0xFF;
}

typedef ulong (*getpixelfunc)(FTfont*, FT_Bitmap*, int, int);

getpixelfunc
getPixelFunc(FTfont* font)
{
	switch (font->rmode) {
	case Rmono:
		return getPixelMono;
	case Rantialias:
		return getPixelAntialias;
	case Rsubpixel:
		return getPixelSubpixel;
	}
	sysfatal("getpixelfunc: bad rmode");
	return nil;
}

void
drawChar(FTfont* font, Memimage* img, int x0, int y0, int c)
{
	int height, width, x, y;
	unsigned long cl;
	Point pt;
	Rectangle r;
	Memimage* color;
	FT_Glyph glyph;
	FT_BitmapGlyph bglyph;
	FT_Bitmap bitmap;
	FT_Vector scale;
	getpixelfunc getPixel;

	glyph = getGlyph(font, c);
	scale = getScale(font);
	bglyph = getBitmapGlyph(font, glyph);
	bitmap = bglyph->bitmap;

	getPixel = getPixelFunc(font);

	r.min.x = 0;
	r.min.y = 0;
	r.max.x = 1;
	r.max.y = 1;
	color = allocmemimage(r, font->image_channels);

	r.min.x = r.min.y = 0;
	r.max.x = bitmap.width;
	r.max.y = bitmap.rows;

	width = bitmap.width / scale.x;
	height = bitmap.rows / scale.y;

	if (font->rmode == Rsubpixel) {
		switch (font->rgb) {
		case Srgb:
		case Sbgr:
			width += 2;
			break;
		case Svrgb:
		case Svbgr:
			height += 2;
			break;
		}
	}

	for(y = 0; y < height; y++) {
		for(x = 0; x < width; x++) {
			cl = getPixel(font, &bitmap, x, y);
			pt.x = x + x0;
			pt.y = y + y0;
			memfillcolor(color, cl);			
			memimagedraw(img, rectaddpt(Rect(0, 0, 1, 1), pt), 
				color, ZP, nil, ZP, SatopD);
		}
	}

	freememimage(color);
}

void
initSubfont(FTfont* font, FTsubfont* sf, int startc)
{
	int i;
	int width;
	int ascent, descent;
	FTmetrics rc[257];

	/* get first defined character */
	if (startc != 0)
		for(; startc<=Runemax; startc++)
			if(FT_Get_Char_Index(font->face, startc) != 0)
				break;

	ascent = 0; 
	descent = 100000;
	width = 0;

	for(i = 0; i < nelem(rc); i++) {
		if (startc+i > Runemax || width>2000)
			break;

		if (i > 0 && FT_Get_Char_Index(font->face, startc + i) == 0) {
			rc[i] = getMetrics(font, 0);
			rc[i].width = 0;
			rc[i].advance = 0;
		} else
			rc[i] = getMetrics(font, startc + i);

			
		if (ascent < rc[i].top)
			ascent = rc[i].top;

		if (descent > rc[i].bottom)
			descent = rc[i].bottom;

		width += rc[i].width; // +1?
	}

	sf->startc = startc;
	sf->nchars = i;
	sf->ascent = ascent;
	sf->descent = descent;
	sf->image_width = width;
	sf->metrics = malloc(i * sizeof(FTmetrics));
	sf->fchars = malloc((i + 1) * sizeof(Fontchar));

	for(i = 0; i < sf->nchars; i++)
		sf->metrics[i] = rc[i];
}

int
createSubfont(FTfont* font, FTsubfont* sf, char* fname)
{
	int i, x, fd;
	Rectangle r;

	sf->image_height = sf->ascent - sf->descent;
//	fprint(2, "creating %d: (%d, %d)\n", sf->startc, sf->image_height, sf->image_width);


	r = Rect(0, 0, sf->image_width, font->height /*sf->image_height*/);
	if (sf->image_width <= 0 || sf->image_height <= 0)
		return 1;

	sf->image = allocmemimage(r, font->image_channels);
	memfillcolor(sf->image, 0x000000FF);

	x = 0;
	for(i = 0; i < sf->nchars; i++) {
		sf->fchars[i].x = x;
		sf->fchars[i].width = sf->metrics[i].advance;
		sf->fchars[i].left = sf->metrics[i].left;
if(sf->fchars[i].left<0)fprint(2, "LEFT < 0 YO YO\n");
		sf->fchars[i].bottom = /*sf->ascent*/ font->ascent - sf->metrics[i].bottom;
		sf->fchars[i].top = (font->ascent - sf->metrics[i].top) > 0 ? font->ascent - sf->metrics[i].top : 0;
		x += sf->metrics[i].width; // +1?

		drawChar(font, sf->image, sf->fchars[i].x, sf->fchars[i].top, i + sf->startc);
	}

	sf->fchars[i].x = x;
	sf->sf.name = font->name;
	sf->sf.n = sf->nchars;
	sf->sf.height = font->height; /*sf->image_height; */
	sf->sf.ascent = font->ascent /*sf->ascent */;
	sf->sf.info = sf->fchars;
	sf->sf.ref = 1;

	fd = create(fname, OWRITE, 0666);
	if(fd < 0)
		sysfatal("can not create font file: %r");

	writememimage(fd, sf->image);
	writesubfont(fd, &sf->sf);
	close(fd);
	freememimage(sf->image);
	sf->image = nil;

	return 0;
}

void
usage(void)
{
	fprint(2, "\n"
	"usage: ttf2subf <options>\n"
	"	-s size			- point size\n"
	"	-h 			- this message\n"
	"	-f fname			- ttf file name\n"
	"	-n name			- font name\n"
	"	-m mono|antialias|subpixel	- rendering mode\n"
	"	-r rgb|bgr|vrgb|vbgr	- LCD display type\n\n");
	exits("usage");
}

static struct {
	Rmode m;
	char* s;
} tab[] = {
	{Rmono,		"mono"},
	{Rantialias,	"antialias"},
	{Rsubpixel,	"subpixel"},
};

static Rmode
strtomode(char *s)
{
	int i;

	for(i = 0; i < nelem(tab); i++)
		if (strcmp(s, tab[i].s) == 0)
			return tab[i].m;
	sysfatal("unknown mode");
	return tab[0].m;
}

static struct {
	Rmode m;
	char* s;
} tab2[] = {
	{Srgb,		"rgb"},
	{Sbgr,	"bgr"},
	{Svrgb,	"vrgb"},
	{Svbgr,	"vbgr"},
};

static SRgb
strtorgb(char *s)
{
	int i;

	for(i = 0; i < nelem(tab2); i++)
		if (strcmp(s, tab2[i].s) == 0)
			return tab2[i].m;
	sysfatal("unknown rgb type");
	return tab2[i].m;
}

void main(int argc, char* argv[]) {
	char *fname, buf[256];
	int status, i, fd;
	FTfont font;
	FTsubfont* sf;
	SRgb srgb;

	memset(&font, 0, sizeof font);
	fname = 0;
	srgb = 0;

	if(argc == 1)
		usage();

	ARGBEGIN {
	case 's':
		font.size = atoi(EARGF(usage()));
		break;
	case 'h':
		usage();
		break;	/* never reached */
	case 'f':
		fname = EARGF(usage());
		break;
	case 'n':
		font.name = EARGF(usage());
		break;
	case 'm':
		font.rmode = strtomode(EARGF(usage()));
		break;
	case 'r':
		srgb = strtorgb(EARGF(usage()));
		break;
	default:
		usage();
	} ARGEND

	if (fname == nil)
		sysfatal("no fount name specified");
	if (font.size == 0)
		sysfatal("no font size specified");

	if (font.name == nil)
		font.name = fname;

	switch (font.rmode) {
	case Rmono:
		font.image_channels = GREY1;
		break;
	case Rantialias:
		font.image_channels = GREY8;
		break;
	case Rsubpixel:
		font.image_channels = RGB24;
		font.rgb = srgb;
		break;
	}

	print("font file: %s, font name: %s, size: %d\n", fname, font.name, font.size);
	memimageinit();
	if((status = FT_Init_FreeType(&lib)) != 0)
		sysfatal("FT_Init_FreeType: status=%d\n", status);
	if(FT_New_Face(lib, fname, 0, &font.face) != 0)
		sysfatal("FT_New_Face: status=%d\n", status);
//	if(FT_Set_Char_Size(font.face, 0, font.size * 64, 72, 72) != 0)
	if(FT_Set_Char_Size(font.face, 0, font.size * 64, 72, 72) != 0)
		sysfatal("FT_Set_Char_Size: status=%d\n", status);

	FT_Select_Charmap(font.face, ft_encoding_unicode);

	font.slot = font.face->glyph;
	font.sfont = nil;
	font.ascent = 0;
	font.descent = 10000;

	for(i =0; i<=Runemax; ){
		FTsubfont* sf = malloc(sizeof *sf);

		initSubfont(&font, sf, i);
		if (sf->nchars == 0)
			break;

		print("last: %d, start: %d, nchars: %d, width: %d\n", i, sf->startc, sf->nchars, sf->image_width);

		i = sf->startc + sf->nchars;
		sf->next = font.sfont;
		font.sfont = sf;

		if (font.ascent < sf->ascent)
			font.ascent = sf->ascent;
		if (font.descent > sf->descent)
			font.descent = sf->descent;
	}

	/*	font.height = font.ascent - font.descent; */
	font.height = font.size;
	font.ascent = font.height-3;

	/* do we have a directory created for this font? */
	snprint(buf, sizeof(buf), "%s", font.name);
	if(access(buf, AEXIST) == -1) {
		fd = create(buf, OREAD, DMDIR|0777);
		if(fd < 0) 
			sysfatal("cannot create font directory: %r");
	}

	snprint(buf, sizeof(buf), "%s/%s.%d.font", font.name, font.name, font.size);
	fd = create(buf, OWRITE, 0666);
	if(fd < 0)
		sysfatal("cannot create .font file: %r");
		
	fprint(fd, "%d\t%d\n", font.height, font.ascent);

	for(sf = font.sfont; sf != nil; sf = sf->next) {
		snprint(buf, sizeof buf, "%s/%s.%d.%04x", font.name, font.name, font.size, sf->startc);
//		fprint("generating 0x%04x - 0x%04x\n", sf->startc, sf->startc + sf->nchars);

		if (createSubfont(&font, sf, buf) == 0) {
			snprint(buf, sizeof(buf), "%s.%d.%04x", font.name, font.size, sf->startc);
			fprint(fd, "0x%04x\t0x%04x\t%s\n", sf->startc, sf->startc+sf->nchars - 1, buf);
		}

	}
	close(fd);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.