code: 9ferno

ref: fcccaf74c54ecd44b7e8bf45a7b0c99572f6a949
dir: /appl/ebook/cssfont.b/

View raw version
implement CSSfont;
include "sys.m";
	sys: Sys;
include "draw.m";
	draw: Draw;
	Font: import draw;
include "units.m";
	units: Units;
include "cssfont.m";

# locally available font styles
BOLD, CW, ITALIC, PLAIN, NSTYLES: con iota;
NSIZES: con 5;			# number of locally available font sizes

fonts := array[] of {
	PLAIN => array[] of {
		"/fonts/charon/plain.tiny.font",
		"/fonts/charon/plain.small.font",
		"/fonts/charon/plain.normal.font",
		"/fonts/charon/plain.large.font",
		"/fonts/charon/plain.vlarge.font",
	},
	BOLD => array[] of {
		"/fonts/charon/bold.tiny.font",
		"/fonts/charon/bold.small.font",
		"/fonts/charon/bold.normal.font",
		"/fonts/charon/bold.large.font",
		"/fonts/charon/bold.vlarge.font",
		},
	CW => array[] of {
		"/fonts/charon/cw.tiny.font",
		"/fonts/charon/cw.small.font",
		"/fonts/charon/cw.normal.font",
		"/fonts/charon/cw.large.font",
		"/fonts/charon/cw.vlarge.font",
		},
	ITALIC => array[] of {
		"/fonts/charon/italic.tiny.font",
		"/fonts/charon/italic.small.font",
		"/fonts/charon/italic.normal.font",
		"/fonts/charon/italic.large.font",
		"/fonts/charon/italic.vlarge.font",
	},
};

fontinfo := array[NSTYLES] of array of ref Font;
sizechoice := array[NSTYLES] of array of byte;

init(displ: ref Draw->Display)
{
	sys = load Sys Sys->PATH;
	draw = load Draw Draw->PATH;
	units = load Units Units->PATH;
	if (units == nil) {
		sys->fprint(sys->fildes(2), "cssfont: cannot load %s: %r\n", Units->PATH);
		raise "fail:bad module";
	}
	units->init();

	for (i := 0; i < len fonts; i++) {
		fpaths := fonts[i];
		fontinfo[i] = array[len fpaths] of ref Font;

		# could make this process lazier, only computing sizes
		# when a font of a particular class was asked for.
		maxheight := 0;
		for (j := 0; j < len fpaths; j++) {
			if ((fontinfo[i][j] = f := Font.open(displ, fpaths[j])) == nil) {
				sys->fprint(sys->fildes(2), "cssfont: font %s unavailable: %r\n", fpaths[j]);
				raise "fail:font unavailable";
			}
			if (f.height > maxheight)
				maxheight = f.height;
		}
		sizechoice[i] = array[maxheight + 1] of byte;
		for (j = 0; j < maxheight + 1; j++)
			sizechoice[i][j] = byte matchheight(j, fontinfo[i]);
	}

#	for (i = 0; i < NSTYLES; i++) {
#		sys->print("class %d\n", i);
#		for (j := 0; j < NSIZES; j++) {
#			sys->print("	height %d; translates to %d [%d]\n",
#				fontinfo[i][j].height,
#				int sizechoice[i][fontinfo[i][j].height],
#				fontinfo[i][int sizechoice[i][fontinfo[i][j].height]].height);
#		}
#	}
}

# find the closest match to a given desired height from the choices given.
matchheight(desired: int, choices: array of ref Font): int
{
	n := len choices;
	if (desired <= choices[0].height)
		return 0;
	if (desired >= choices[n - 1].height)
		return n - 1;
	for (i := 1; i < n; i++) {
		if (desired >= choices[i - 1].height &&
				desired <= choices[i].height) {
			if (desired - choices[i - 1].height <
					choices[i].height - desired)
				return i - 1;
			else
				return i;
		}
	}
	sys->fprint(sys->fildes(2), "cssfont: can't happen!\n");
	raise "error";
	return -1;		# should never happen
}

# get an appropriate font given the css specification.
getfont(spec: Spec, parentem, parentex: int): (string, int, int)
{
#sys->print("getfont size:%s family:%s; style:%s; weight:%s -> ",
#		spec.size, spec.family, spec.style, spec.weight);
	class := getclass(spec);
	i := choosesize(class, spec.size, parentem, parentex);

#sys->print("%s (height:%d)\n", fonts[class][i], fontinfo[class][i].height);

	# XXX i suppose we should really find out what height(widgth?) the 'x' is.
	return (fonts[class][i], fontinfo[class][i].height, fontinfo[class][i].height);
}

getclass(spec: Spec): int
{
	if (spec.family == "monospace")
		return CW;
	if (spec.style == "italic")
		return ITALIC;
	if (spec.weight == "bold")
		return BOLD;
	return PLAIN;
}

choosesize(class: int, size: string, parentem, parentex: int): int
{
	if (size != nil && (size[0] >= '0' && size[0] <= '9')) {
		(height, nil) := units->length(size, parentem, parentex, nil);
		choices := sizechoice[class];
		if (height > len choices)
			height = len choices - 1;
		return int choices[height];
	}
	case size {
	"xx-small" or
	"x-small" =>
		return 0;
	"small" =>
		return 1;
	"medium" =>
		return 2;
	"large" =>
		return 3;
	"x-large" or
	"xx-large" =>
		return 4;
	"larger" or
	"smaller" =>
		choice := sizechoice[class];
		if (parentem >= len choice)
			parentem = len choice - 1;
		i := int choice[parentem];
		if (size[0] == 's') {
			if (i > 0)
				i--;
		} else {
			if (i < len fonts[class] - 1)
				i++;
		}
		return i;
	* =>
		sys->fprint(sys->fildes(2), "cssfont: unknown font size spec '%s'\n", size);
		return 2;
	}
}