Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 16 Jun 2019 09:17:26 +0000 (UTC)
From:      Ed Maste <emaste@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r349100 - head/usr.bin/vtfontcvt
Message-ID:  <201906160917.x5G9HQo2039846@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: emaste
Date: Sun Jun 16 09:17:26 2019
New Revision: 349100
URL: https://svnweb.freebsd.org/changeset/base/349100

Log:
  vtfontcvt: improve BDF and hex font parsing
  
  Support larger font sizes.
  
  PR:		205707
  Submitted by:	Dmitry Wagin (original version)
  MFC after:	2 weeks
  Event:		Berlin Devsummit 2019
  Differential Revision:	https://reviews.freebsd.org/D20650

Modified:
  head/usr.bin/vtfontcvt/vtfontcvt.c

Modified: head/usr.bin/vtfontcvt/vtfontcvt.c
==============================================================================
--- head/usr.bin/vtfontcvt/vtfontcvt.c	Sun Jun 16 05:12:17 2019	(r349099)
+++ head/usr.bin/vtfontcvt/vtfontcvt.c	Sun Jun 16 09:17:26 2019	(r349100)
@@ -223,119 +223,237 @@ add_char(unsigned curchar, unsigned map_idx, uint8_t *
 	return (0);
 }
 
-
+/*
+ * Right-shift glyph row by _shift_ bits. Row _len_ bits wide, _size_ bytes.
+ */
 static int
-parse_bitmap_line(uint8_t *left, uint8_t *right, unsigned int line,
-    unsigned int dwidth)
+rshift_row(uint8_t *line, size_t size, size_t len, size_t shift)
 {
-	uint8_t *p;
-	unsigned int i, subline;
+	size_t d, s, i;
+	uint16_t t;
 
-	if (dwidth != width && dwidth != width * 2)
-		errx(1, "Bitmap with unsupported width %u!", dwidth);
+	assert(size > 0 && len > 0);
+	assert(size * 8 >= len);
 
-	/* Move pixel data right to simplify splitting double characters. */
-	line >>= (howmany(dwidth, 8) * 8) - dwidth;
+	if (shift == 0)
+		return (0);
 
-	for (i = dwidth / width; i > 0; i--) {
-		p = (i == 2) ? right : left;
+	d = shift / 8;
+	s = 8 - shift % 8;
+	i = howmany(len, 8);
 
-		subline = line & ((1 << width) - 1);
-		subline <<= (howmany(width, 8) * 8) - width;
+	while (i > 0) {
+		i--;
 
-		if (wbytes == 1) {
-			*p = subline;
-		} else if (wbytes == 2) {
-			*p++ = subline >> 8;
-			*p = subline;
-		} else {
-			errx(1, "Unsupported wbytes %u!", wbytes);
-		}
+		t = *(line + i);
+		*(line + i) = 0;
 
-		line >>= width;
+		t <<= s;
+
+		if (i + d + 1 < size)
+			*(line + i + d + 1) |= (uint8_t)t;
+		if (i + d < size)
+			*(line + i + d) = t >> 8;
 	}
+	return (0);
+}
 
+/*
+ * Split double-width characters into left and right half. Single-width
+ * characters in _left_ only.
+ */
+static int
+split_row(uint8_t *left, uint8_t *right, uint8_t *line, size_t w)
+{
+	size_t s, i;
+
+	s = wbytes * 8 - width;
+
+	memcpy(left, line, wbytes);
+	*(left + wbytes - 1) &= 0xFF << s;
+
+	if (w > width) { /* Double-width character. */
+		uint8_t t;
+
+		for (i = 0; i < wbytes; i++) {
+			t = *(line + wbytes + i - 1);
+			t <<= 8 - s;
+			t |= *(line + wbytes + i) >> s;
+			*(right + i) = t;
+		}
+		*(right + wbytes - 1) &= 0xFF << s;
+	}
 	return (0);
 }
 
+static void
+set_height(int h)
+{
+	if (h <= 0 || h > VFNT_MAXDIMENSION)
+		errx(1, "invalid height %d", h);
+	height = h;
+}
+
+static void
+set_width(int w)
+{
+	if (w <= 0 || w > VFNT_MAXDIMENSION)
+		errx(1, "invalid width %d", w);
+	width = w;
+	wbytes = howmany(width, 8);
+}
+
 static int
 parse_bdf(FILE *fp, unsigned int map_idx)
 {
-	char *ln;
+	char *line, *ln, *p;
 	size_t length;
-	uint8_t bytes[wbytes * height], bytes_r[wbytes * height];
-	unsigned int curchar = 0, dwidth = 0, i, line;
+	uint8_t *bytes, *bytes_r;
+	unsigned int curchar = 0, i, j, linenum = 0, bbwbytes;
+	int bbw, bbh, bbox, bboy;		/* Glyph bounding box. */
+	int fbbw = 0, fbbh, fbbox, fbboy;	/* Font bounding box. */
+	int dwidth = 0, dwy = 0;
+	int rv;
+	char spc = '\0';
 
+	/*
+	 * Step 1: Parse FONT logical font descriptor and FONTBOUNDINGBOX
+	 * bounding box.
+	 */
 	while ((ln = fgetln(fp, &length)) != NULL) {
+		linenum++;
 		ln[length - 1] = '\0';
 
-		if (strncmp(ln, "ENCODING ", 9) == 0) {
-			curchar = atoi(ln + 9);
+		if (strncmp(ln, "FONT ", 5) == 0) {
+			p = ln + 5;
+			i = 0;
+			while ((p = strchr(p, '-')) != NULL) {
+				p++;
+				i++;
+				if (i == 11) {
+					spc = *p;
+					break;
+				}
+			}
+		} else if (strncmp(ln, "FONTBOUNDINGBOX ", 16) == 0 &&
+		    sscanf(ln + 16, "%d %d %d %d", &fbbw, &fbbh, &fbbox,
+		    &fbboy) == 4) {
+			set_width(fbbw);
+			set_height(fbbh);
+			break;
 		}
+	}
+	if (fbbw == 0)
+		errx(1, "broken font header");
+	if (spc != 'c' && spc != 'C')
+		errx(1, "font spacing \"C\" (character cell) required");
 
-		if (strncmp(ln, "DWIDTH ", 7) == 0) {
-			dwidth = atoi(ln + 7);
+	/* Step 2: Validate DWIDTH (Device Width) of all glyphs. */
+	while ((ln = fgetln(fp, &length)) != NULL) {
+		linenum++;
+		ln[length - 1] = '\0';
+
+		if (strncmp(ln, "DWIDTH ", 7) == 0 &&
+		    sscanf(ln + 7, "%d %d", &dwidth, &dwy) == 2) {
+			if (dwy != 0 || (dwidth != fbbw && dwidth * 2 != fbbw))
+				errx(1, "bitmap with unsupported DWIDTH %d %d at line %u",
+				    dwidth, dwy, linenum);
+			if (dwidth < fbbw)
+				set_width(dwidth);
 		}
+	}
 
-		if (strncmp(ln, "BITMAP", 6) == 0 &&
+	/* Step 3: Restart at the beginning of the file and read glyph data. */
+	dwidth = bbw = bbh = 0;
+	rewind(fp);
+	linenum = 0;
+	bytes = xmalloc(wbytes * height);
+	bytes_r = xmalloc(wbytes * height);
+	line = xmalloc(wbytes * 2);
+	while ((ln = fgetln(fp, &length)) != NULL) {
+		linenum++;
+		ln[length - 1] = '\0';
+
+		if (strncmp(ln, "ENCODING ", 9) == 0) {
+			curchar = atoi(ln + 9);
+		} else if (strncmp(ln, "DWIDTH ", 7) == 0) {
+			dwidth = atoi(ln + 7);
+		} else if (strncmp(ln, "BBX ", 4) == 0 &&
+		    sscanf(ln + 4, "%d %d %d %d", &bbw, &bbh, &bbox,
+		     &bboy) == 4) {
+			if (bbw < 1 || bbh < 1 || bbw > fbbw || bbh > fbbh ||
+			    bbox < fbbox || bboy < fbboy)
+				errx(1, "broken bitmap with BBX %d %d %d %d at line %u",
+				    bbw, bbh, bbox, bboy, linenum);
+			bbwbytes = howmany(bbw, 8);
+		} else if (strncmp(ln, "BITMAP", 6) == 0 &&
 		    (ln[6] == ' ' || ln[6] == '\0')) {
+			if (dwidth == 0 || bbw == 0 || bbh == 0)
+				errx(1, "broken char header at line %u!",
+				    linenum);
+			memset(bytes, 0, wbytes * height);
+			memset(bytes_r, 0, wbytes * height);
+
 			/*
-			 * Assume that the next _height_ lines are bitmap
-			 * data.  ENDCHAR is allowed to terminate the bitmap
+			 * Assume that the next _bbh_ lines are bitmap data.
+			 * ENDCHAR is allowed to terminate the bitmap
 			 * early but is not otherwise checked; any extra data
 			 * is ignored.
 			 */
-			for (i = 0; i < height; i++) {
+			for (i = (fbbh + fbboy) - (bbh + bboy);
+			    i < (unsigned int)((fbbh + fbboy) - bboy); i++) {
 				if ((ln = fgetln(fp, &length)) == NULL)
-					errx(1, "Unexpected EOF!");
+					errx(1, "unexpected EOF");
+				linenum++;
 				ln[length - 1] = '\0';
-				if (strcmp(ln, "ENDCHAR") == 0) {
-					memset(bytes + i * wbytes, 0,
-					    (height - i) * wbytes);
-					memset(bytes_r + i * wbytes, 0,
-					    (height - i) * wbytes);
+				if (strcmp(ln, "ENDCHAR") == 0)
 					break;
+				if (strlen(ln) < bbwbytes * 2)
+					errx(1, "broken bitmap at line %u",
+					    linenum);
+				memset(line, 0, wbytes * 2);
+				for (j = 0; j < bbwbytes; j++) {
+					unsigned int val;
+					if (sscanf(ln + j * 2, "%2x", &val) ==
+					    0)
+						break;
+					*(line + j) = (uint8_t)val;
 				}
-				sscanf(ln, "%x", &line);
-				if (parse_bitmap_line(bytes + i * wbytes,
-				     bytes_r + i * wbytes, line, dwidth) != 0)
-					return (1);
+
+				rv = rshift_row(line, wbytes * 2, bbw,
+				    bbox - fbbox);
+				if (rv != 0)
+					goto out;
+
+				rv = split_row(bytes + i * wbytes,
+				     bytes_r + i * wbytes, line, dwidth);
+				if (rv != 0)
+					goto out;
 			}
 
-			if (add_char(curchar, map_idx, bytes,
-			    dwidth == width * 2 ? bytes_r : NULL) != 0)
-				return (1);
+			rv = add_char(curchar, map_idx, bytes,
+			    dwidth > (int)width ? bytes_r : NULL);
+			if (rv != 0)
+				goto out;
+
+			dwidth = bbw = bbh = 0;
 		}
 	}
 
-	return (0);
+out:
+	free(bytes);
+	free(bytes_r);
+	free(line);
+	return (rv);
 }
 
-static void
-set_height(int h)
-{
-	if (h <= 0 || h > VFNT_MAXDIMENSION)
-		errx(1, "invalid height %d", h);
-	height = h;
-}
-
-static void
-set_width(int w)
-{
-	if (w <= 0 || w > VFNT_MAXDIMENSION)
-		errx(1, "invalid width %d", w);
-	width = w;
-	wbytes = howmany(width, 8);
-}
-
 static int
 parse_hex(FILE *fp, unsigned int map_idx)
 {
 	char *ln, *p;
-	char fmt_str[8];
 	size_t length;
-	uint8_t *bytes = NULL, *bytes_r = NULL;
-	unsigned curchar = 0, i, line, chars_per_row, dwidth;
+	uint8_t *bytes = NULL, *bytes_r = NULL, *line = NULL;
+	unsigned curchar = 0, gwidth, gwbytes, i, j, chars_per_row;
 	int rv = 0;
 
 	while ((ln = fgetln(fp, &length)) != NULL) {
@@ -353,36 +471,46 @@ parse_hex(FILE *fp, unsigned int map_idx)
 			if (bytes == NULL) {
 				bytes = xmalloc(wbytes * height);
 				bytes_r = xmalloc(wbytes * height);
+				line = xmalloc(wbytes * 2);
 			}
 			/* ln is guaranteed to have a colon here. */
 			p = strchr(ln, ':') + 1;
 			chars_per_row = strlen(p) / height;
-			dwidth = width;
-			if (chars_per_row / 2 > (width + 7) / 8)
-				dwidth *= 2; /* Double-width character. */
-			snprintf(fmt_str, sizeof(fmt_str), "%%%ux",
-			    chars_per_row);
+			if (chars_per_row < wbytes * 2)
+				errx(1,
+				    "malformed input: broken bitmap, character %06x",
+				    curchar);
+			gwidth = width * 2;
+			gwbytes = howmany(width, 8);
+			if (chars_per_row < gwbytes * 2 || gwidth <= 8) {
+				gwidth = width; /* Single-width character. */
+				gwbytes = wbytes;
+			}
 
 			for (i = 0; i < height; i++) {
-				sscanf(p, fmt_str, &line);
-				p += chars_per_row;
-				if (parse_bitmap_line(bytes + i * wbytes,
-				    bytes_r + i * wbytes, line, dwidth) != 0) {
-					rv = 1;
-					goto out;
+				for (j = 0; j < gwbytes; j++) {
+					unsigned int val;
+					if (sscanf(p + j * 2, "%2x", &val) == 0)
+						break;
+					*(line + j) = (uint8_t)val;
 				}
+				rv = split_row(bytes + i * wbytes,
+				    bytes_r + i * wbytes, line, gwidth);
+				if (rv != 0)
+					goto out;
+				p += gwbytes * 2;
 			}
 
-			if (add_char(curchar, map_idx, bytes,
-			    dwidth == width * 2 ? bytes_r : NULL) != 0) {
-				rv = 1;
+			rv = add_char(curchar, map_idx, bytes,
+			    gwidth != width ? bytes_r : NULL);
+			if (rv != 0)
 				goto out;
-			}
 		}
 	}
 out:
 	free(bytes);
 	free(bytes_r);
+	free(line);
 	return (rv);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906160917.x5G9HQo2039846>