Index: uspace/lib/gfxfont/include/gfx/text.h
===================================================================
--- uspace/lib/gfxfont/include/gfx/text.h	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/gfxfont/include/gfx/text.h	(revision 4afb6c91df0d969238da3aab915a9e09020645bf)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,4 +46,8 @@
 extern errno_t gfx_puttext(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
     const char *);
+extern void gfx_text_start_pos(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_coord2_t *);
+extern size_t gfx_text_find_pos(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_coord2_t *);
 
 #endif
Index: uspace/lib/gfxfont/src/text.c
===================================================================
--- uspace/lib/gfxfont/src/text.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/gfxfont/src/text.c	(revision 4afb6c91df0d969238da3aab915a9e09020645bf)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -167,5 +167,5 @@
 }
 
-/** Render text.
+/** Get text starting position.
  *
  * @param font Font
@@ -173,4 +173,57 @@
  * @param fmt Text formatting
  * @param str String
+ * @param spos Place to store starting position
+ * @return EOK on success or an error code
+ */
+void gfx_text_start_pos(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *spos)
+{
+	gfx_font_metrics_t fmetrics;
+	gfx_coord2_t cpos;
+	gfx_coord_t width;
+
+	*spos = *pos;
+
+	/* Adjust position for horizontal alignment */
+	if (fmt->halign != gfx_halign_left) {
+		width = gfx_text_width(font, str);
+		switch (fmt->halign) {
+		case gfx_halign_center:
+			spos->x -= width / 2;
+			break;
+		case gfx_halign_right:
+			spos->x -= width - 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Adjust position for vertical alignment */
+	gfx_font_get_metrics(font, &fmetrics);
+
+	if (fmt->valign != gfx_valign_baseline) {
+		switch (fmt->valign) {
+		case gfx_valign_top:
+			spos->y += fmetrics.ascent;
+			break;
+		case gfx_valign_center:
+			spos->y += fmetrics.ascent / 2;
+			break;
+		case gfx_valign_bottom:
+			cpos.y -= fmetrics.descent;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/** Render text.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
  * @return EOK on success or an error code
  */
@@ -178,5 +231,4 @@
     gfx_text_fmt_t *fmt, const char *str)
 {
-	gfx_font_metrics_t fmetrics;
 	gfx_glyph_metrics_t gmetrics;
 	size_t stradv;
@@ -184,42 +236,7 @@
 	gfx_glyph_t *glyph;
 	gfx_coord2_t cpos;
-	gfx_coord_t width;
 	errno_t rc;
 
-	cpos = *pos;
-
-	/* Adjust position for horizontal alignment */
-	if (fmt->halign != gfx_halign_left) {
-		width = gfx_text_width(font, str);
-		switch (fmt->halign) {
-		case gfx_halign_center:
-			cpos.x -= width / 2;
-			break;
-		case gfx_halign_right:
-			cpos.x -= width - 1;
-			break;
-		default:
-			break;
-		}
-	}
-
-	/* Adjust position for vertical alignment */
-	gfx_font_get_metrics(font, &fmetrics);
-
-	if (fmt->valign != gfx_valign_baseline) {
-		switch (fmt->valign) {
-		case gfx_valign_top:
-			cpos.y += fmetrics.ascent;
-			break;
-		case gfx_valign_center:
-			cpos.y += fmetrics.ascent / 2;
-			break;
-		case gfx_valign_bottom:
-			cpos.y -= fmetrics.descent;
-			break;
-		default:
-			break;
-		}
-	}
+	gfx_text_start_pos(font, pos, fmt, str, &cpos);
 
 	/* Text mode */
@@ -252,4 +269,68 @@
 }
 
+/** Find character position in string by X coordinate.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
+ * @param fpos Position for which we need to find offset
+ *
+ * @return Byte offset in @a str of character corresponding to position
+ *         @a fpos. Note that the position is rounded, that is,
+ *         if it is before the center of character A, it will return
+ *         offset of A, if it is after the center of A, it will return
+ *         offset of the following character.
+ */
+size_t gfx_text_find_pos(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *fpos)
+{
+	gfx_glyph_metrics_t gmetrics;
+	size_t stradv;
+	const char *cp;
+	gfx_glyph_t *glyph;
+	gfx_coord2_t cpos;
+	size_t off;
+	size_t strsize;
+	errno_t rc;
+
+	gfx_text_start_pos(font, pos, fmt, str, &cpos);
+
+	/* Text mode */
+	if ((font->finfo->props.flags & gff_text_mode) != 0) {
+		off = 0;
+		strsize = str_size(str);
+		while (off < strsize) {
+			if (fpos->x <= cpos.x)
+				return off;
+			(void) str_decode(str, &off, strsize);
+			cpos.x++;
+		}
+
+		return off;
+	}
+
+	cp = str;
+	off = 0;
+	while (*cp != '\0') {
+		rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
+		if (rc != EOK) {
+			++cp;
+			continue;
+		}
+
+		gfx_glyph_get_metrics(glyph, &gmetrics);
+
+		if (fpos->x < cpos.x + gmetrics.advance / 2)
+			return off;
+
+		cp += stradv;
+		off += stradv;
+		cpos.x += gmetrics.advance;
+	}
+
+	return off;
+}
+
 /** @}
  */
Index: uspace/lib/gfxfont/test/text.c
===================================================================
--- uspace/lib/gfxfont/test/text.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/gfxfont/test/text.c	(revision 4afb6c91df0d969238da3aab915a9e09020645bf)
@@ -30,4 +30,5 @@
 #include <gfx/context.h>
 #include <gfx/font.h>
+#include <gfx/glyph.h>
 #include <gfx/text.h>
 #include <gfx/typeface.h>
@@ -144,4 +145,210 @@
 	gfx_typeface_destroy(tface);
 	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_start_pos() correctly computes text start position */
+PCUT_TEST(text_start_pos)
+{
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t pos;
+	test_gc_t tgc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_props_init(&props);
+	gfx_font_metrics_init(&metrics);
+	metrics.ascent = 10; // XXX
+	metrics.descent = 10; // XXX
+	rc = gfx_font_create(tface, &props, &metrics, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
+	pos.x = 0;
+	pos.y = 0;
+
+	rc = gfx_puttext(font, &pos, &fmt, "Hello world!");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_find_pos() finds position in text */
+PCUT_TEST(text_find_pos)
+{
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_glyph_metrics_t gmetrics;
+	gfx_glyph_t *glyph1;
+	gfx_glyph_t *glyph2;
+	gfx_context_t *gc;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_coord2_t fpos;
+	size_t off;
+	test_gc_t tgc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	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);
+
+	/* Need to create some glyphs with metrics */
+	gfx_glyph_metrics_init(&gmetrics);
+	gmetrics.advance = 10;
+
+	rc = gfx_glyph_create(font, &gmetrics, &glyph1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_glyph_set_pattern(glyph1, "A");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_glyph_metrics_init(&gmetrics);
+	gmetrics.advance = 1;
+
+	rc = gfx_glyph_create(font, &gmetrics, &glyph2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_glyph_set_pattern(glyph2, "i");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_text_fmt_init(&fmt);
+	anchor.x = 10;
+	anchor.y = 0;
+
+	fpos.x = 9;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 10;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 11;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 19;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(1, off);
+
+	fpos.x = 20;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(2, off);
+
+	fpos.x = 21;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	fpos.x = 22;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	gfx_glyph_destroy(glyph1);
+	gfx_glyph_destroy(glyph2);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_find_pos() finds position in text in text mode */
+PCUT_TEST(text_find_pos_text)
+{
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	test_gc_t tgc;
+	size_t off;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_coord2_t fpos;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_font_create_textmode(tface, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	anchor.x = 10;
+	anchor.y = 0;
+	gfx_text_fmt_init(&fmt);
+
+	fpos.x = 9;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 10;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 11;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(1, off);
+
+	fpos.x = 12;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(2, off);
+
+	fpos.x = 13;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	fpos.x = 14;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
 
 	rc = gfx_context_delete(gc);
