Index: uspace/lib/gfxfont/include/gfx/typeface.h
===================================================================
--- uspace/lib/gfxfont/include/gfx/typeface.h	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/include/gfx/typeface.h	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -48,4 +48,6 @@
 extern gfx_font_info_t *gfx_typeface_first_font(gfx_typeface_t *);
 extern gfx_font_info_t *gfx_typeface_next_font(gfx_font_info_t *);
+extern errno_t gfx_typeface_open(gfx_context_t *, const char *,
+    gfx_typeface_t **);
 extern errno_t gfx_typeface_save(gfx_typeface_t *, const char *);
 
Index: uspace/lib/gfxfont/meson.build
===================================================================
--- uspace/lib/gfxfont/meson.build	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/meson.build	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -40,4 +40,5 @@
 	'test/glyph_bmp.c',
 	'test/main.c',
+	'test/tpf.c',
 	'test/typeface.c',
 )
Index: uspace/lib/gfxfont/private/font.h
===================================================================
--- uspace/lib/gfxfont/private/font.h	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/private/font.h	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -59,4 +59,6 @@
 	/** Containing typeface */
 	struct gfx_typeface *typeface;
+	/** Containing font info */
+	struct gfx_font_info *finfo;
 	/** Font metrics */
 	gfx_font_metrics_t metrics;
@@ -82,8 +84,12 @@
 	/** Font or @c NULL if font is not present in memory */
 	struct gfx_font *font;
+	/** Font chunk from which font can be read */
+	riff_rchunk_t fontck;
 };
 
 extern errno_t gfx_font_splice_at_glyph(gfx_font_t *, gfx_glyph_t *,
     gfx_rect_t *);
+extern errno_t gfx_font_info_load(gfx_typeface_t *, riff_rchunk_t *);
+extern errno_t gfx_font_load(gfx_font_info_t *);
 extern errno_t gfx_font_save(gfx_font_info_t *, riffw_t *);
 
Index: uspace/lib/gfxfont/private/glyph.h
===================================================================
--- uspace/lib/gfxfont/private/glyph.h	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/private/glyph.h	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -77,4 +77,5 @@
 extern errno_t gfx_glyph_transfer(gfx_glyph_t *, gfx_coord_t, gfx_bitmap_t *,
     gfx_rect_t *);
+extern errno_t gfx_glyph_load(gfx_font_t *, riff_rchunk_t *);
 extern errno_t gfx_glyph_save(gfx_glyph_t *, riffw_t *);
 
Index: uspace/lib/gfxfont/private/typeface.h
===================================================================
--- uspace/lib/gfxfont/private/typeface.h	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/private/typeface.h	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -40,4 +40,5 @@
 #include <adt/list.h>
 #include <errno.h>
+#include <riff/chunk.h>
 #include <types/gfx/bitmap.h>
 #include <types/gfx/context.h>
@@ -57,4 +58,6 @@
 	/** Fonts (of gfx_font_info_t) */
 	list_t fonts;
+	/** RIFF reader of the open typeface file or @c NULL */
+	riffr_t *riffr;
 };
 
Index: uspace/lib/gfxfont/src/font.c
===================================================================
--- uspace/lib/gfxfont/src/font.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/src/font.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -81,7 +81,8 @@
 }
 
-/** Create font.
+/** Create font with existing font info structure.
  *
  * @param tface Typeface
+ * @param finfo Font info
  * @param metrics Font metrics
  * @param rfont Place to store pointer to new font
@@ -91,17 +92,10 @@
  *         was lost
  */
-errno_t gfx_font_create(gfx_typeface_t *tface, gfx_font_props_t *props,
-    gfx_font_metrics_t *metrics, gfx_font_t **rfont)
-{
-	gfx_font_info_t *finfo = NULL;
+static errno_t gfx_font_create_with_info(gfx_typeface_t *tface,
+    gfx_font_info_t *finfo, gfx_font_metrics_t *metrics, gfx_font_t **rfont)
+{
 	gfx_font_t *font = NULL;
 	gfx_bitmap_params_t params;
 	errno_t rc;
-
-	finfo = calloc(1, sizeof(gfx_font_info_t));
-	if (finfo == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
 
 	font = calloc(1, sizeof(gfx_font_t));
@@ -110,10 +104,6 @@
 		goto error;
 	}
-
-	finfo->typeface = tface;
-	finfo->props = *props;
-	finfo->font = font;
-
 	font->typeface = tface;
+	font->finfo = finfo;
 
 	font->rect.p0.x = 0;
@@ -138,4 +128,44 @@
 	font->metrics = *metrics;
 	list_initialize(&font->glyphs);
+	*rfont = font;
+	return EOK;
+error:
+	if (font != NULL)
+		free(font);
+	return rc;
+}
+
+/** Create font.
+ *
+ * @param tface Typeface
+ * @param props Font properties
+ * @param metrics Font metrics
+ * @param rfont Place to store pointer to new font
+ *
+ * @return EOK on success, EINVAL if parameters are invald,
+ *         ENOMEM if insufficient resources, EIO if graphic device connection
+ *         was lost
+ */
+errno_t gfx_font_create(gfx_typeface_t *tface, gfx_font_props_t *props,
+    gfx_font_metrics_t *metrics, gfx_font_t **rfont)
+{
+	gfx_font_info_t *finfo = NULL;
+	gfx_font_t *font = NULL;
+	errno_t rc;
+
+	finfo = calloc(1, sizeof(gfx_font_info_t));
+	if (finfo == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	finfo->typeface = tface;
+	finfo->props = *props;
+
+	rc = gfx_font_create_with_info(tface, finfo, metrics, &font);
+	if (rc != EOK)
+		goto error;
+
+	finfo->font = font;
 	list_append(&finfo->lfonts, &tface->fonts);
 	*rfont = font;
@@ -144,6 +174,4 @@
 	if (finfo != NULL)
 		free(finfo);
-	if (font != NULL)
-		free(font);
 	return rc;
 }
@@ -157,11 +185,14 @@
 errno_t gfx_font_open(gfx_font_info_t *finfo, gfx_font_t **rfont)
 {
+	errno_t rc;
+
 	if (finfo->font == NULL) {
-		/*
-		 * We cannot load an absent font yet.
-		 * This should not happen.
-		 */
-		assert(false);
-		return ENOTSUP;
+		/* Load absent font from TPF file */
+		rc = gfx_font_load(finfo);
+		if (rc != EOK)
+			return rc;
+
+		assert(finfo->font != NULL);
+		finfo->font->finfo = finfo;
 	}
 
@@ -184,4 +215,5 @@
 	}
 
+	font->finfo->font = NULL;
 	free(font);
 }
@@ -360,4 +392,32 @@
 }
 
+/** Load font properties from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param props Font properties
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_props_load(riff_rchunk_t *parent,
+    gfx_font_props_t *props)
+{
+	errno_t rc;
+	riff_rchunk_t propsck;
+	size_t nread;
+
+	rc = riff_rchunk_match(parent, CKID_fprp, &propsck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_read(&propsck, (void *) props, sizeof(*props), &nread);
+	if (rc != EOK || nread != sizeof(*props))
+		return EIO;
+
+	rc = riff_rchunk_end(&propsck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Save font properties to RIFF TPF file.
  *
@@ -386,4 +446,32 @@
 }
 
+/** Load font metrics from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param metrics Font metrics
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_metrics_load(riff_rchunk_t *parent,
+    gfx_font_metrics_t *metrics)
+{
+	errno_t rc;
+	riff_rchunk_t mtrck;
+	size_t nread;
+
+	rc = riff_rchunk_match(parent, CKID_fmtr, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_read(&mtrck, (void *) metrics, sizeof(*metrics), &nread);
+	if (rc != EOK || nread != sizeof(*metrics))
+		return EIO;
+
+	rc = riff_rchunk_end(&mtrck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Save font metrics to RIFF TPF file.
  *
@@ -413,4 +501,81 @@
 }
 
+/** Load font bitmap from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param font Font
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_bitmap_load(riff_rchunk_t *parent, gfx_font_t *font)
+{
+	errno_t rc;
+	riff_rchunk_t bmpck;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bitmap = NULL;
+	uint32_t width;
+	uint32_t height;
+	uint32_t depth;
+	size_t nread;
+
+	rc = riff_rchunk_match(parent, CKID_fbmp, &bmpck);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_read_uint32(&bmpck, &width);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_read_uint32(&bmpck, &height);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_read_uint32(&bmpck, &depth);
+	if (rc != EOK)
+		goto error;
+
+	if (depth != 8 * sizeof(uint32_t)) {
+		rc = ENOTSUP;
+		goto error;
+	}
+
+	gfx_bitmap_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = width;
+	params.rect.p1.y = height;
+
+	rc = gfx_bitmap_create(font->typeface->gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_read(&bmpck, (void *) alloc.pixels,
+	    width * height * sizeof(uint32_t), &nread);
+	if (rc != EOK)
+		goto error;
+
+	if (nread != width * height * sizeof(uint32_t)) {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = riff_rchunk_end(&bmpck);
+	if (rc != EOK)
+		goto error;
+
+	gfx_bitmap_destroy(font->bitmap);
+	font->bitmap = bitmap;
+	font->rect = params.rect;
+	return EOK;
+error:
+	if (bitmap != NULL)
+		gfx_bitmap_destroy(bitmap);
+	return rc;
+}
+
 /** Save font bitmap to RIFF TPF file.
  *
@@ -457,8 +622,105 @@
 }
 
+/** Load font info from RIFF TPF file.
+ *
+ * @param tface Containing typeface
+ * @param parent Parent chunk
+ * @return EOK on success or an error code
+ */
+errno_t gfx_font_info_load(gfx_typeface_t *tface, riff_rchunk_t *parent)
+{
+	errno_t rc;
+	riff_rchunk_t fontck;
+	gfx_font_props_t props;
+	gfx_font_info_t *finfo = NULL;
+	gfx_font_t *font = NULL;
+
+	rc = riff_rchunk_list_match(parent, LTYPE_font, &fontck);
+	if (rc != EOK)
+		goto error;
+
+	finfo = calloc(1, sizeof(gfx_font_info_t));
+	if (finfo == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	finfo->fontck = fontck;
+
+	rc = gfx_font_props_load(&fontck, &props);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_rchunk_end(&fontck);
+	if (rc != EOK)
+		goto error;
+
+	finfo->typeface = tface;
+	list_append(&finfo->lfonts, &tface->fonts);
+	finfo->props = props;
+	finfo->font = NULL;
+
+	return EOK;
+error:
+	if (finfo != NULL)
+		free(finfo);
+	if (font != NULL)
+		gfx_font_close(font);
+	return rc;
+}
+
+/** Load font from RIFF TPF file.
+ *
+ * @param finfo Font information
+ * @return EOK on success or an error code
+ */
+errno_t gfx_font_load(gfx_font_info_t *finfo)
+{
+	errno_t rc;
+	gfx_font_metrics_t metrics;
+	gfx_font_t *font = NULL;
+
+	/* Seek to beginning of chunk (just after list type) */
+	rc = riff_rchunk_seek(&finfo->fontck, sizeof(uint32_t), SEEK_SET);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_font_props_load(&finfo->fontck, &finfo->props);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_font_metrics_load(&finfo->fontck, &metrics);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_font_create_with_info(finfo->typeface, finfo, &metrics, &font);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_font_bitmap_load(&finfo->fontck, font);
+	if (rc != EOK)
+		goto error;
+
+	while (true) {
+		rc = gfx_glyph_load(font, &finfo->fontck);
+		if (rc == ENOENT)
+			break;
+		if (rc != EOK)
+			goto error;
+	}
+
+	finfo->font = font;
+	return EOK;
+error:
+	if (font != NULL)
+		gfx_font_close(font);
+	return rc;
+}
+
 /** Save font into RIFF TPF file.
  *
  * @param finfo Font info
  * @param riffw RIFF writer
+ * @return EOK on success or an error code
  */
 errno_t gfx_font_save(gfx_font_info_t *finfo, riffw_t *riffw)
Index: uspace/lib/gfxfont/src/glyph.c
===================================================================
--- uspace/lib/gfxfont/src/glyph.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/src/glyph.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -315,4 +315,32 @@
 }
 
+/** Load glyph metrics from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param metrics Place to store glyph metrics
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_metrics_load(riff_rchunk_t *parent,
+    gfx_glyph_metrics_t *metrics)
+{
+	errno_t rc;
+	riff_rchunk_t mtrck;
+	size_t nread;
+
+	rc = riff_rchunk_match(parent, CKID_gmtr, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_read(&mtrck, (void *) metrics, sizeof(*metrics), &nread);
+	if (rc != EOK || nread != sizeof(*metrics))
+		return EIO;
+
+	rc = riff_rchunk_end(&mtrck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Save glyph metrics to RIFF TPF file.
  *
@@ -342,4 +370,55 @@
 }
 
+/** Load glyph patterns from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param glyph Glyph
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_patterns_load(riff_rchunk_t *parent,
+    gfx_glyph_t *glyph)
+{
+	errno_t rc;
+	riff_rchunk_t patck;
+	uint32_t cksize;
+	size_t i;
+	size_t nread;
+	char *buf = NULL;
+
+	rc = riff_rchunk_match(parent, CKID_gpat, &patck);
+	if (rc != EOK)
+		goto error;
+
+	cksize = riff_rchunk_size(&patck);
+	buf = malloc(cksize);
+	if (buf == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = riff_read(&patck, buf, cksize, &nread);
+	if (rc != EOK || nread != cksize)
+		goto error;
+
+	i = 0;
+	while (i < cksize) {
+		rc = gfx_glyph_set_pattern(glyph, &buf[i]);
+		if (rc != EOK)
+			goto error;
+
+		i += str_size(&buf[i]) + 1;
+	}
+
+	rc = riff_rchunk_end(&patck);
+	if (rc != EOK)
+		goto error;
+
+	free(buf);
+	return EOK;
+error:
+	if (buf != NULL)
+		free(buf);
+	return rc;
+}
 /** Save glyph patterns to RIFF TPF file.
  *
@@ -377,4 +456,38 @@
 }
 
+/** Load glyph rectangle/origin from RIFF TPF file.
+ *
+ * @param parent Parent chunk
+ * @param glyph Glyph
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_rectangle_origin_load(riff_rchunk_t *parent,
+    gfx_glyph_t *glyph)
+{
+	errno_t rc;
+	riff_rchunk_t rorck;
+	size_t nread;
+
+	rc = riff_rchunk_match(parent, CKID_gror, &rorck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_read(&rorck, (void *) &glyph->rect, sizeof(glyph->rect),
+	    &nread);
+	if (rc != EOK || nread != sizeof(glyph->rect))
+		return EIO;
+
+	rc = riff_read(&rorck, (void *) &glyph->origin, sizeof(glyph->origin),
+	    &nread);
+	if (rc != EOK || nread != sizeof(glyph->origin))
+		return EIO;
+
+	rc = riff_rchunk_end(&rorck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Save glyph rectangle/origin to RIFF TPF file.
  *
@@ -408,8 +521,53 @@
 }
 
+/** Load glyph from RIFF TPF file.
+ *
+ * @param font Containing font
+ * @param parent Parent chunk
+ * @return EOK on success or an error code
+ */
+errno_t gfx_glyph_load(gfx_font_t *font, riff_rchunk_t *parent)
+{
+	errno_t rc;
+	gfx_glyph_metrics_t metrics;
+	gfx_glyph_t *glyph = NULL;
+	riff_rchunk_t glyphck;
+
+	rc = riff_rchunk_list_match(parent, LTYPE_glph, &glyphck);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_glyph_metrics_load(&glyphck, &metrics);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_glyph_create(font, &metrics, &glyph);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_glyph_patterns_load(&glyphck, glyph);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_glyph_rectangle_origin_load(&glyphck, glyph);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_rchunk_end(&glyphck);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	if (glyph != NULL)
+		gfx_glyph_destroy(glyph);
+	return rc;
+}
+
 /** Save glyph into RIFF TPF file.
  *
  * @param glyph Glyph
  * @param riffw RIFF writer
+ * @return EOK on success or an error code
  */
 errno_t gfx_glyph_save(gfx_glyph_t *glyph, riffw_t *riffw)
Index: uspace/lib/gfxfont/src/typeface.c
===================================================================
--- uspace/lib/gfxfont/src/typeface.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/src/typeface.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -38,4 +38,5 @@
 #include <errno.h>
 #include <gfx/bitmap.h>
+#include <gfx/font.h>
 #include <gfx/glyph.h>
 #include <gfx/typeface.h>
@@ -78,4 +79,19 @@
 void gfx_typeface_destroy(gfx_typeface_t *tface)
 {
+	gfx_font_info_t *finfo;
+
+	if (tface->riffr != NULL)
+		(void) riff_rclose(tface->riffr);
+
+	finfo = gfx_typeface_first_font(tface);
+	while (finfo != NULL) {
+		if (finfo->font != NULL)
+			gfx_font_close(finfo->font);
+		list_remove(&finfo->lfonts);
+		free(finfo);
+
+		finfo = gfx_typeface_first_font(tface);
+	}
+
 	free(tface);
 }
@@ -111,4 +127,88 @@
 
 	return list_get_instance(link, gfx_font_info_t, lfonts);
+}
+
+/** Open typeface from a TPF file.
+ *
+ * @param gc Graphic context
+ * @param fname File name
+ * @param rtface Place to store pointer to open typeface
+ * @return EOK on success or an error code
+ */
+errno_t gfx_typeface_open(gfx_context_t *gc, const char *fname,
+    gfx_typeface_t **rtface)
+{
+	riffr_t *riffr = NULL;
+	gfx_typeface_t *tface = NULL;
+	errno_t rc;
+	riff_rchunk_t riffck;
+	uint32_t format;
+
+	rc = gfx_typeface_create(gc, &tface);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_ropen(fname, &riffck, &riffr);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_read_uint32(&riffck, &format);
+	if (rc != EOK)
+		goto error;
+
+	if (format != FORM_TPFC) {
+		rc = ENOTSUP;
+		goto error;
+	}
+
+	while (true) {
+		rc = gfx_font_info_load(tface, &riffck);
+		if (rc == ENOENT)
+			break;
+
+		if (rc != EOK)
+			goto error;
+	}
+
+	rc = riff_rchunk_end(&riffck);
+	if (rc != EOK)
+		goto error;
+
+	tface->riffr = riffr;
+	*rtface = tface;
+	return EOK;
+error:
+	if (riffr != NULL)
+		riff_rclose(riffr);
+	if (tface != NULL)
+		gfx_typeface_destroy(tface);
+	return rc;
+}
+
+/** Make sure all typeface fonts are loaded.
+ *
+ * @param tface Typeface
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_typeface_loadin(gfx_typeface_t *tface)
+{
+	gfx_font_t *font;
+	gfx_font_info_t *finfo;
+	errno_t rc;
+
+	finfo = gfx_typeface_first_font(tface);
+	while (finfo != NULL) {
+		/* Open font to make sure it is loaded */
+		rc = gfx_font_open(finfo, &font);
+		if (rc != EOK)
+			return rc;
+
+		/* Don't need this anymore */
+		(void)font;
+
+		finfo = gfx_typeface_next_font(finfo);
+	}
+
+	return EOK;
 }
 
@@ -125,4 +225,12 @@
 	gfx_font_info_t *finfo;
 	riff_wchunk_t riffck;
+
+	/*
+	 * Make sure all fonts are loaded before writing (in case
+	 * we are writing into our original backing file).
+	 */
+	rc = gfx_typeface_loadin(tface);
+	if (rc != EOK)
+		return rc;
 
 	rc = riff_wopen(fname, &riffw);
Index: uspace/lib/gfxfont/test/main.c
===================================================================
--- uspace/lib/gfxfont/test/main.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/gfxfont/test/main.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -34,4 +34,5 @@
 PCUT_IMPORT(glyph);
 PCUT_IMPORT(glyph_bmp);
+PCUT_IMPORT(tpf);
 PCUT_IMPORT(typeface);
 
Index: uspace/lib/gfxfont/test/tpf.c
===================================================================
--- uspace/lib/gfxfont/test/tpf.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
+++ uspace/lib/gfxfont/test/tpf.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2020 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gfx/context.h>
+#include <gfx/font.h>
+#include <gfx/glyph.h>
+#include <gfx/typeface.h>
+#include <pcut/pcut.h>
+#include "../private/font.h"
+#include "../private/typeface.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(tpf);
+
+static errno_t testgc_set_color(void *, gfx_color_t *);
+static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t testgc_bitmap_destroy(void *);
+static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t test_ops = {
+	.set_color = testgc_set_color,
+	.fill_rect = testgc_fill_rect,
+	.bitmap_create = testgc_bitmap_create,
+	.bitmap_destroy = testgc_bitmap_destroy,
+	.bitmap_render = testgc_bitmap_render,
+	.bitmap_get_alloc = testgc_bitmap_get_alloc
+};
+
+typedef struct {
+	gfx_bitmap_params_t bm_params;
+	void *bm_pixels;
+	gfx_rect_t bm_srect;
+	gfx_coord2_t bm_offs;
+} test_gc_t;
+
+typedef struct {
+	test_gc_t *tgc;
+	gfx_bitmap_alloc_t alloc;
+	bool myalloc;
+} testgc_bitmap_t;
+
+/** Test saving typeface to and loading from TPF file */
+PCUT_TEST(save_load)
+{
+	char fname[L_tmpnam];
+	char *p;
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+	gfx_glyph_metrics_t gmetrics;
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_font_info_t *finfo;
+	gfx_context_t *gc;
+	gfx_glyph_t *glyph;
+	test_gc_t tgc;
+	errno_t rc;
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_metrics_init(&metrics);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_props_init(&props);
+	gfx_font_metrics_init(&metrics);
+	rc = gfx_font_create(tface, &props, &metrics, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_glyph_metrics_init(&gmetrics);
+	rc = gfx_glyph_create(font, &gmetrics, &glyph);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_save(tface, fname);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_typeface_destroy(tface);
+	tface = NULL;
+
+	rc = gfx_typeface_open(gc, fname, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(tface);
+
+	finfo = gfx_typeface_first_font(tface);
+	PCUT_ASSERT_NOT_NULL(finfo);
+
+	rc = gfx_font_open(finfo, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(font);
+
+	glyph = gfx_font_first_glyph(font);
+	PCUT_ASSERT_NOT_NULL(glyph);
+
+	gfx_typeface_destroy(tface);
+	tface = NULL;
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	(void) remove(p);
+}
+
+static errno_t testgc_set_color(void *arg, gfx_color_t *color)
+{
+	return EOK;
+}
+
+static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
+}
+
+static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+	testgc_bitmap_t *tbm;
+
+	tbm = calloc(1, sizeof(testgc_bitmap_t));
+	if (tbm == NULL)
+		return ENOMEM;
+
+	if (alloc == NULL) {
+		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
+		    sizeof(uint32_t);
+		tbm->alloc.off0 = 0;
+		tbm->alloc.pixels = calloc(sizeof(uint32_t),
+		    tbm->alloc.pitch * (params->rect.p1.y - params->rect.p0.y));
+		tbm->myalloc = true;
+		if (tbm->alloc.pixels == NULL) {
+			free(tbm);
+			return ENOMEM;
+		}
+	} else {
+		tbm->alloc = *alloc;
+	}
+
+	tbm->tgc = tgc;
+	tgc->bm_params = *params;
+	tgc->bm_pixels = tbm->alloc.pixels;
+	*rbm = (void *)tbm;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_destroy(void *bm)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	if (tbm->myalloc)
+		free(tbm->alloc.pixels);
+	free(tbm);
+	return EOK;
+}
+
+static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	tbm->tgc->bm_srect = *srect;
+	tbm->tgc->bm_offs = *offs;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	*alloc = tbm->alloc;
+	return EOK;
+}
+
+PCUT_EXPORT(tpf);
Index: uspace/lib/riff/include/riff/chunk.h
===================================================================
--- uspace/lib/riff/include/riff/chunk.h	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/riff/include/riff/chunk.h	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -55,4 +55,5 @@
 extern errno_t riff_rchunk_list_match(riff_rchunk_t *, riff_ltype_t,
     riff_rchunk_t *);
+extern errno_t riff_rchunk_seek(riff_rchunk_t *, long, int);
 extern errno_t riff_rchunk_end(riff_rchunk_t *);
 extern errno_t riff_read(riff_rchunk_t *, void *, size_t, size_t *);
Index: uspace/lib/riff/src/chunk.c
===================================================================
--- uspace/lib/riff/src/chunk.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/riff/src/chunk.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -393,4 +393,44 @@
 }
 
+/** Seek to position in chunk.
+ *
+ * @param rchunk RIFF chunk
+ * @param offset Offset
+ * @param whence SEEK_SET, SEEK_CUR or SEEK_END
+ * @return EOK on success or an error code
+ */
+errno_t riff_rchunk_seek(riff_rchunk_t *rchunk, long offset, int whence)
+{
+	long pos;
+	long dest;
+	int rv;
+
+	switch (whence) {
+	case SEEK_SET:
+		dest = rchunk->ckstart + offset;
+		break;
+	case SEEK_END:
+		dest = rchunk->ckstart + rchunk->cksize + offset;
+		break;
+	case SEEK_CUR:
+		pos = ftell(rchunk->riffr->f);
+		if (pos < 0)
+			return EIO;
+		dest = pos + offset;
+		break;
+	default:
+		return EINVAL;
+	}
+
+	if (dest < rchunk->ckstart || dest > rchunk->ckstart + rchunk->cksize)
+		return ELIMIT;
+
+	rv = fseek(rchunk->riffr->f, dest, SEEK_SET);
+	if (rv < 0)
+		return EIO;
+
+	return EOK;
+}
+
 /** Return chunk data size.
  *
@@ -431,5 +471,6 @@
 /** Finish reading RIFF chunk.
  *
- * Seek to the first byte after end of chunk.
+ * Seek to the first byte after end of chunk. It is allowed, though,
+ * to return to the chunk later, e.g. using riff_rchunk_seek(@a rchunk, ..).
  *
  * @param rchunk Chunk structure
@@ -444,5 +485,4 @@
 		return EIO;
 
-	rchunk->riffr = NULL;
 	return EOK;
 }
Index: uspace/lib/riff/test/chunk.c
===================================================================
--- uspace/lib/riff/test/chunk.c	(revision aaf962e6b6b4ce31f98624e6c289f5d564a6aa86)
+++ uspace/lib/riff/test/chunk.c	(revision ea459d4465f0f9c069c883c1feed74658fed24e0)
@@ -544,3 +544,109 @@
 }
 
+/** Seek back to different positions in a chunk */
+PCUT_TEST(rchunk_seek)
+{
+	char fname[L_tmpnam];
+	char *p;
+	riffw_t *rw;
+	riffr_t *rr;
+	riff_wchunk_t wriffck;
+	riff_wchunk_t wdatack;
+	riff_rchunk_t rriffck;
+	riff_rchunk_t rdatack;
+	uint32_t rword;
+	errno_t rc;
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	/* Write RIFF file */
+
+	rc = riff_wopen(p, &rw);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(rw);
+
+	rc = riff_wchunk_start(rw, CKID_RIFF, &wriffck);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Write data chunk */
+
+	rc = riff_wchunk_start(rw, CKID_dat1, &wdatack);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_write_uint32(rw, 1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_write_uint32(rw, 2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_write_uint32(rw, 3);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_write_uint32(rw, 4);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_wchunk_end(rw, &wdatack);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_wchunk_end(rw, &wriffck);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_wclose(rw);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Read back RIFF file */
+
+	rc = riff_ropen(p, &rriffck, &rr);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(rr);
+
+	PCUT_ASSERT_INT_EQUALS(CKID_RIFF, rriffck.ckid);
+
+	/* Read data chunk */
+
+	rc = riff_rchunk_start(&rriffck, &rdatack);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(CKID_dat1, rdatack.ckid);
+
+	rc = riff_read_uint32(&rdatack, &rword);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(1, rword);
+
+	rc = riff_rchunk_end(&rdatack);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Try reading first word of data chunk again */
+
+	rc = riff_rchunk_seek(&rdatack, 0, SEEK_SET);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_read_uint32(&rdatack, &rword);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(1, rword);
+
+	/* Try reading last word of data chunk */
+
+	rc = riff_rchunk_seek(&rdatack, -4, SEEK_END);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_read_uint32(&rdatack, &rword);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(4, rword);
+
+	/* Try reading previous word of data chunk */
+
+	rc = riff_rchunk_seek(&rdatack, -8, SEEK_CUR);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = riff_read_uint32(&rdatack, &rword);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(3, rword);
+
+	rc = riff_rclose(rr);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	(void) remove(p);
+}
+
 PCUT_EXPORT(chunk);
