Index: uspace/lib/gfxfont/include/gfx/typeface.h
===================================================================
--- uspace/lib/gfxfont/include/gfx/typeface.h	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/include/gfx/typeface.h	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -48,4 +48,5 @@
 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_save(gfx_typeface_t *, const char *);
 
 #endif
Index: uspace/lib/gfxfont/meson.build
===================================================================
--- uspace/lib/gfxfont/meson.build	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/meson.build	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'gfx' ]
+deps = [ 'gfx', 'riff' ]
 src = files(
 	'src/font.c',
Index: uspace/lib/gfxfont/private/font.h
===================================================================
--- uspace/lib/gfxfont/private/font.h	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/private/font.h	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -44,4 +44,5 @@
 #include <types/gfx/font.h>
 #include <types/gfx/typeface.h>
+#include <riff/chunk.h>
 
 /** Font
@@ -85,4 +86,5 @@
 extern errno_t gfx_font_splice_at_glyph(gfx_font_t *, gfx_glyph_t *,
     gfx_rect_t *);
+extern errno_t gfx_font_save(gfx_font_info_t *, riffw_t *);
 
 #endif
Index: uspace/lib/gfxfont/private/glyph.h
===================================================================
--- uspace/lib/gfxfont/private/glyph.h	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/private/glyph.h	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -41,4 +41,5 @@
 #include <gfx/coord.h>
 #include <types/gfx/glyph.h>
+#include <riff/chunk.h>
 
 /** Glyph
@@ -76,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_save(gfx_glyph_t *, riffw_t *);
 
 #endif
Index: uspace/lib/gfxfont/private/tpf_file.h
===================================================================
--- uspace/lib/gfxfont/private/tpf_file.h	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
+++ uspace/lib/gfxfont/private/tpf_file.h	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/** @addtogroup libgfxfont
+ * @{
+ */
+/**
+ * @file TPF file definitions
+ *
+ */
+
+#ifndef _GFX_PRIVATE_TPF_FILE_H
+#define _GFX_PRIVATE_TPF_FILE_H
+
+enum {
+	/** Typeface RIFF format ID */
+	FORM_TPFC = 0x43465054,
+
+	/** Font list type */
+	LTYPE_font = 0x746e6f66,
+
+	/** Font properties chunk ID */
+	CKID_fprp = 0x70727066,
+	/** Font metrics chunk ID */
+	CKID_fmtr = 0x72746d66,
+	/** Font bitmap chunk ID */
+	CKID_fbmp = 0x706d6266,
+
+	/** Glyph list type */
+	LTYPE_glph = 0x68706c67,
+
+	/** Glyph metrics chunk ID */
+	CKID_gmtr = 0x72746d67,
+	/** Glyph patterns chunk ID */
+	CKID_gpat = 0x74617067,
+	/** Glyph rectangle/origin chunk ID */
+	CKID_gror = 0x726f7267
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfxfont/src/font.c
===================================================================
--- uspace/lib/gfxfont/src/font.c	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/src/font.c	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -44,4 +44,5 @@
 #include "../private/font.h"
 #include "../private/glyph.h"
+#include "../private/tpf_file.h"
 #include "../private/typeface.h"
 
@@ -359,4 +360,148 @@
 }
 
+/** Save font properties to RIFF TPF file.
+ *
+ * @param props Font properties
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_props_save(gfx_font_props_t *props, riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t propsck;
+
+	rc = riff_wchunk_start(riffw, CKID_fprp, &propsck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) props, sizeof(*props));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &propsck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save font metrics to RIFF TPF file.
+ *
+ * @param metrics Font metrics
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_metrics_save(gfx_font_metrics_t *metrics,
+    riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t mtrck;
+
+	rc = riff_wchunk_start(riffw, CKID_fmtr, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) metrics, sizeof(*metrics));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save font bitmap to RIFF TPF file.
+ *
+ * @param font Font
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_font_bitmap_save(gfx_font_t *font, riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t bmpck;
+	gfx_bitmap_alloc_t alloc;
+
+	rc = gfx_bitmap_get_alloc(font->bitmap, &alloc);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_start(riffw, CKID_fbmp, &bmpck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_write_uint32(riffw, font->rect.p1.x);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_write_uint32(riffw, font->rect.p1.y);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_write_uint32(riffw, 8 * sizeof(uint32_t));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) alloc.pixels,
+	    font->rect.p1.x * font->rect.p1.y * sizeof(uint32_t));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &bmpck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save font into RIFF TPF file.
+ *
+ * @param finfo Font info
+ * @param riffw RIFF writer
+ */
+errno_t gfx_font_save(gfx_font_info_t *finfo, riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t fontck;
+	gfx_glyph_t *glyph;
+
+	rc = riff_wchunk_start(riffw, CKID_LIST, &fontck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_write_uint32(riffw, LTYPE_font);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_font_props_save(&finfo->props, riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_font_metrics_save(&finfo->font->metrics, riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_font_bitmap_save(finfo->font, riffw);
+	if (rc != EOK)
+		return rc;
+
+	glyph = gfx_font_first_glyph(finfo->font);
+	while (glyph != NULL) {
+		rc = gfx_glyph_save(glyph, riffw);
+		if (rc != EOK)
+			return rc;
+
+		glyph = gfx_font_next_glyph(glyph);
+	}
+
+	rc = riff_wchunk_end(riffw, &fontck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/lib/gfxfont/src/glyph.c
===================================================================
--- uspace/lib/gfxfont/src/glyph.c	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/src/glyph.c	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -44,4 +44,5 @@
 #include "../private/font.h"
 #include "../private/glyph.h"
+#include "../private/tpf_file.h"
 
 /** Initialize glyph metrics structure.
@@ -314,4 +315,136 @@
 }
 
+/** Save glyph metrics to RIFF TPF file.
+ *
+ * @param metrics Glyph metrics
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_metrics_save(gfx_glyph_metrics_t *metrics,
+    riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t mtrck;
+
+	rc = riff_wchunk_start(riffw, CKID_gmtr, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) metrics, sizeof(*metrics));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &mtrck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save glyph patterns to RIFF TPF file.
+ *
+ * @param glyph Glyph
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_patterns_save(gfx_glyph_t *glyph, riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t patck;
+	gfx_glyph_pattern_t *pat;
+	const char *str;
+
+	rc = riff_wchunk_start(riffw, CKID_gpat, &patck);
+	if (rc != EOK)
+		return rc;
+
+	pat = gfx_glyph_first_pattern(glyph);
+	while (pat != NULL) {
+		str = gfx_glyph_pattern_str(pat);
+
+		rc = riff_wchunk_write(riffw, (void *) str, 1 + str_size(str));
+		if (rc != EOK)
+			return rc;
+
+		pat = gfx_glyph_next_pattern(pat);
+	}
+
+	rc = riff_wchunk_end(riffw, &patck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save glyph rectangle/origin to RIFF TPF file.
+ *
+ * @param glyph Glyph
+ * @param riffw RIFF writer
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_glyph_rectangle_origin_save(gfx_glyph_t *glyph,
+    riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t rorck;
+
+	rc = riff_wchunk_start(riffw, CKID_gror, &rorck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) &glyph->rect,
+	    sizeof(glyph->rect));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_write(riffw, (void *) &glyph->origin,
+	    sizeof(glyph->origin));
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &rorck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Save glyph into RIFF TPF file.
+ *
+ * @param glyph Glyph
+ * @param riffw RIFF writer
+ */
+errno_t gfx_glyph_save(gfx_glyph_t *glyph, riffw_t *riffw)
+{
+	errno_t rc;
+	riff_wchunk_t glyphck;
+
+	rc = riff_wchunk_start(riffw, CKID_LIST, &glyphck);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_write_uint32(riffw, LTYPE_glph);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_glyph_metrics_save(&glyph->metrics, riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_glyph_patterns_save(glyph, riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_glyph_rectangle_origin_save(glyph, riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_end(riffw, &glyphck);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/lib/gfxfont/src/typeface.c
===================================================================
--- uspace/lib/gfxfont/src/typeface.c	(revision 313ac8e5226a24618b305138fc1a126ba71dc2e7)
+++ uspace/lib/gfxfont/src/typeface.c	(revision 003c4139d7ad847545f542bf5da3be65eb920764)
@@ -41,7 +41,9 @@
 #include <gfx/typeface.h>
 #include <mem.h>
+#include <riff/chunk.h>
 #include <stdlib.h>
 #include "../private/font.h"
 #include "../private/glyph.h"
+#include "../private/tpf_file.h"
 #include "../private/typeface.h"
 
@@ -111,4 +113,54 @@
 }
 
+/** Save typeface into a TPF file.
+ *
+ * @param tface Typeface
+ * @param fname Destination file name
+ * @return EOK on success or an error code
+ */
+errno_t gfx_typeface_save(gfx_typeface_t *tface, const char *fname)
+{
+	riffw_t *riffw = NULL;
+	errno_t rc;
+	gfx_font_info_t *finfo;
+	riff_wchunk_t riffck;
+
+	rc = riff_wopen(fname, &riffw);
+	if (rc != EOK)
+		return rc;
+
+	rc = riff_wchunk_start(riffw, CKID_RIFF, &riffck);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_write_uint32(riffw, FORM_TPFC);
+	if (rc != EOK)
+		goto error;
+
+	finfo = gfx_typeface_first_font(tface);
+	while (finfo != NULL) {
+		/* Save font */
+		rc = gfx_font_save(finfo, riffw);
+		if (rc != EOK)
+			goto error;
+
+		finfo = gfx_typeface_next_font(finfo);
+	}
+
+	rc = riff_wchunk_end(riffw, &riffck);
+	if (rc != EOK)
+		goto error;
+
+	rc = riff_wclose(riffw);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+error:
+	if (riffw != NULL)
+		riff_wclose(riffw);
+	return rc;
+}
+
 /** @}
  */
