Browse Source

Add support for fallback fonts

Léo Gaspard 8 years ago
parent
commit
086f1c6a9e
4 changed files with 77 additions and 27 deletions
  1. 6 2
      TODO
  2. 62 17
      dtext.c
  3. 3 3
      dtext.h
  4. 6 5
      test.c

+ 6 - 2
TODO

@@ -1,6 +1,10 @@
 Still to do:
  * Cleanly report Xlib errors
- * Support for bold and italics
- * Support for fallback fonts
+ * Setup a scheme for error reporting
+ * Cleanup bounding box handling: only return length, and compute height on a
+   font-wide basis?
+ * In dt_load, gracefully handle case when one face refuses to load (ie. free
+   already allocated stuff before returning an error)
+ * Support for faces other than the first
  * Document
  * Fix Fantasque Sans Mono ascent/descent (or is it a font issue?)

+ 62 - 17
dtext.c

@@ -13,6 +13,7 @@
 
 #include "dtext.h"
 
+static dt_error load_face(dt_context *ctx, FT_Face *face, char const *name);
 static dt_error load_char(dt_context *ctx, dt_font *fnt, wchar_t c);
 static uint16_t hash_get(dt_row map[DT_HASH_SIZE], wchar_t key, uint16_t def);
 static dt_error hash_set(dt_row map[DT_HASH_SIZE], wchar_t key, uint16_t val);
@@ -73,40 +74,48 @@ dt_quit(dt_context *ctx)
 }
 
 dt_error
-dt_load(dt_context *ctx, dt_font **res, uint8_t size, char const *name)
+dt_load(dt_context *ctx, dt_font **res, char const *name)
 {
 	dt_error err;
 	dt_font *fnt;
+	size_t i;
+	size_t len;
+	char *face;
 
 	if (!(fnt = malloc(sizeof(*fnt))))
 		return -ENOMEM;
 
-	if ((err = FT_New_Face(ctx->ft_lib, name, 0, &fnt->face)))
-		goto fail_new_face;
-	if ((err = FT_Set_Char_Size(fnt->face, size << 6, 0, 0, 0)))
-		goto fail_char_size;
+	fnt->num_faces = 1;
+	for (i = 0; name[i]; ++i)
+		fnt->num_faces += (name[i] == ';');
+
+	fnt->faces = malloc(fnt->num_faces * sizeof(fnt->faces[0]));
+	for (i = 0; i < fnt->num_faces; ++i) {
+		len = strchr(name, ';') - name;
+		face = strndup(name, len);
+		if ((err = load_face(ctx, &fnt->faces[i], face)))
+			return err;
+		name += len + 1;
+		free(face);
+	}
 
 	fnt->gs = XRenderCreateGlyphSet(ctx->dpy, ctx->argb32_format);
 	memset(fnt->advance, 0, sizeof(fnt->advance));
 
 	*res = fnt;
 	return 0;
-
-fail_char_size:
-	FT_Done_Face(fnt->face); // if this fails... just ignore
-fail_new_face:
-	free(fnt);
-	return err;
 }
 
 dt_error
 dt_free(dt_context *ctx, dt_font *fnt)
 {
 	dt_error err = 0;
+	size_t i;
 
 	XRenderFreeGlyphSet(ctx->dpy, fnt->gs);
 
-	err = FT_Done_Face(fnt->face);
+	for (i = 0; i < fnt->num_faces; ++i)
+		err += FT_Done_Face(fnt->faces[i]);
 
 	free(fnt);
 
@@ -126,7 +135,7 @@ dt_box(dt_context *ctx, dt_font *fnt, dt_bbox *bbox, wchar_t const *txt)
 	memset(bbox, 0, sizeof(*bbox));
 
 	bbox->x = 0;
-	bbox->y = - (fnt->face->ascender >> 6);
+	bbox->y = - (fnt->faces[0]->ascender >> 6);
 
 	for (i = 0; i < len; ++i) {
 		if ((err = load_char(ctx, fnt, txt[i])))
@@ -134,7 +143,7 @@ dt_box(dt_context *ctx, dt_font *fnt, dt_bbox *bbox, wchar_t const *txt)
 		bbox->w += hash_get(fnt->advance, txt[i], 0);
 	}
 
-	bbox->h = fnt->face->height >> 6;
+	bbox->h = fnt->faces[0]->height >> 6;
 
 	return 0;
 }
@@ -176,11 +185,39 @@ dt_draw(dt_context *ctx, dt_font *fnt, dt_style const *style,
 	return 0;
 }
 
+static dt_error
+load_face(dt_context *ctx, FT_Face *face, char const *name) {
+	dt_error err;
+	char *file;
+	char *colon;
+	size_t size;
+
+	colon = strchr(name, ':');
+	if (!colon)
+		return -EINVAL;
+
+	file = strndup(name, colon - name);
+	name = colon + 1;
+
+	size = strtoul(name, 0, 10);
+
+	if ((err = FT_New_Face(ctx->ft_lib, file, 0, face)))
+		return err;
+	if ((err = FT_Set_Char_Size(*face, size << 6, 0, 0, 0))) {
+		FT_Done_Face(*face);
+		return err;
+	}
+
+	return 0;
+}
+
+
 static dt_error
 load_char(dt_context *ctx, dt_font *fnt, wchar_t c)
 {
 	dt_error err;
-	FT_GlyphSlot slot;
+	FT_UInt code;
+	FT_GlyphSlot slot = 0;
 	Glyph gid;
 	XGlyphInfo g;
 	char *img;
@@ -189,9 +226,17 @@ load_char(dt_context *ctx, dt_font *fnt, wchar_t c)
 	if (hash_get(fnt->advance, c, 0))
 		return 0;
 
-	if ((err = FT_Load_Char(fnt->face, c, FT_LOAD_RENDER)))
+	for (i = 0; i < fnt->num_faces; ++i) {
+		code = FT_Get_Char_Index(fnt->faces[i], c);
+		if (!code)
+			continue;
+
+		if ((err = FT_Load_Glyph(fnt->faces[i], code, FT_LOAD_RENDER)))
+			continue;
+		slot = fnt->faces[i]->glyph;
+	}
+	if (!slot)
 		return err;
-	slot = fnt->face->glyph;
 
 	gid = c;
 

+ 3 - 3
dtext.h

@@ -26,7 +26,8 @@ typedef struct {
 
 #define DT_HASH_SIZE 128
 typedef struct {
-	FT_Face face;
+	FT_Face *faces;
+	size_t num_faces;
 
 	GlyphSet gs;
 	dt_row advance[DT_HASH_SIZE];
@@ -50,8 +51,7 @@ typedef struct {
 dt_error dt_init(dt_context **ctx, Display *dpy, Window win);
 dt_error dt_quit(dt_context *ctx);
 
-dt_error dt_load(dt_context *ctx, dt_font **fnt,
-                 uint8_t size, char const *name);
+dt_error dt_load(dt_context *ctx, dt_font **fnt, char const *name);
 dt_error dt_free(dt_context *ctx, dt_font *fnt);
 
 dt_error dt_box(dt_context *ctx, dt_font *fnt, dt_bbox *bbox,

+ 6 - 5
test.c

@@ -12,10 +12,11 @@
 
 #include "dtext.h"
 
-#define TEXT L"The quick brown fox jumps over the lazy dog."
-//#define FONT "/usr/share/fonts/fantasque-sans-mono/FantasqueSansMono-Regular.otf"
-#define FONT "/usr/share/fonts/inconsolata/Inconsolata-Regular.ttf"
-//#define FONT "/usr/share/fonts/libertine/LinLibertine_R.otf"
+#define TEXT L"The quick brown fox jumps over the lazy dog. "
+//#define FONT "/usr/share/fonts/fantasque-sans-mono/FantasqueSansMono-Regular.otf:16"
+#define FONT "/usr/share/fonts/inconsolata/Inconsolata-Regular.ttf:16;" \
+             "/usr/share/fonts/powerline-symbols/PowerlineSymbols.otf:14"
+//#define FONT "/usr/share/fonts/libertine/LinLibertine_R.otf:16"
 
 Display *dpy;
 Window win;
@@ -77,7 +78,7 @@ static void setup_dt()
 {
 	assert(!dt_init(&ctx, dpy, win));
 
-	assert(!dt_load(ctx, &fnt, 16, FONT));
+	assert(!dt_load(ctx, &fnt, FONT));
 
 	memset(&style, 0, sizeof(style));
 	memset(&style_inv, 0, sizeof(style_inv));