diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index d8347630..1a75f5af 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -33,6 +33,10 @@ vte_terminal_set_scroll_on_keystroke
 vte_terminal_get_scroll_on_keystroke
 vte_terminal_set_rewrap_on_resize
 vte_terminal_get_rewrap_on_resize
+vte_terminal_set_cell_height_scale
+vte_terminal_get_cell_height_scale
+vte_terminal_set_cell_width_scale
+vte_terminal_get_cell_width_scale
 vte_terminal_set_color_bold
 vte_terminal_set_color_foreground
 vte_terminal_set_color_background
diff --git a/src/vte.cc b/src/vte.cc
index e4fa4301..0f4b7e98 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -7582,7 +7582,9 @@ VteTerminalPrivate::ensure_font()
 			m_fontdirty = FALSE;
 			_vte_draw_set_text_font (m_draw,
                                                  m_widget,
-					m_fontdesc);
+                                                 m_fontdesc,
+                                                 m_cell_width_scale,
+                                                 m_cell_height_scale);
 			_vte_draw_get_text_metrics (m_draw,
 						    &width, &height, &ascent);
 			apply_font_metrics(width, height, ascent, height - ascent);
@@ -7692,6 +7694,40 @@ VteTerminalPrivate::set_font_scale(gdouble scale)
         return true;
 }
 
+bool
+VteTerminalPrivate::set_cell_width_scale(gdouble scale)
+{
+        /* FIXME: compare old and new scale in pixel space */
+        if (_vte_double_equal(scale, m_cell_width_scale))
+                return false;
+
+        m_cell_width_scale = scale;
+        /* Set the drawing font. */
+        m_fontdirty = TRUE;
+        if (widget_realized()) {
+                ensure_font();
+        }
+
+        return true;
+}
+
+bool
+VteTerminalPrivate::set_cell_height_scale(gdouble scale)
+{
+        /* FIXME: compare old and new scale in pixel space */
+        if (_vte_double_equal(scale, m_cell_height_scale))
+                return false;
+
+        m_cell_height_scale = scale;
+        /* Set the drawing font. */
+        m_fontdirty = TRUE;
+        if (widget_realized()) {
+                ensure_font();
+        }
+
+        return true;
+}
+
 /* Read and refresh our perception of the size of the PTY. */
 void
 VteTerminalPrivate::refresh_size()
@@ -8146,6 +8182,8 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
         m_unscaled_font_desc = nullptr;
         m_fontdesc = nullptr;
         m_font_scale = 1.;
+        m_cell_width_scale = 1.;
+        m_cell_height_scale = 1.;
 	m_has_fonts = FALSE;
 
         m_allow_hyperlink = FALSE;
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index 156358e4..d71b2889 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -209,6 +209,18 @@ void vte_terminal_set_font_scale(VteTerminal *terminal,
 _VTE_PUBLIC
 gdouble vte_terminal_get_font_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 
+_VTE_PUBLIC
+void vte_terminal_set_cell_width_scale(VteTerminal *terminal,
+                                       gdouble scale) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+gdouble vte_terminal_get_cell_width_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+
+_VTE_PUBLIC
+void vte_terminal_set_cell_height_scale(VteTerminal *terminal,
+                                        gdouble scale) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+gdouble vte_terminal_get_cell_height_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+
 /* Set various on-off settings. */
 _VTE_PUBLIC
 void vte_terminal_set_audible_bell(VteTerminal *terminal,
diff --git a/src/vtedefines.hh b/src/vtedefines.hh
index 3afc5328..89e64161 100644
--- a/src/vtedefines.hh
+++ b/src/vtedefines.hh
@@ -95,6 +95,8 @@
 
 #define VTE_FONT_SCALE_MIN (.25)
 #define VTE_FONT_SCALE_MAX (4.)
+#define VTE_CELL_SCALE_MIN (1.)
+#define VTE_CELL_SCALE_MAX (2.)
 
 /* Maximum length of a URI in the OSC 8 escape sequences. There's no de jure limit,
  * 2000-ish the de facto standard, and Internet Explorer supports 2083.
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index bf53a7dd..8faaa6d9 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -241,7 +241,7 @@ struct font_info {
 	struct unistr_info ascii_unistr_info[128];
 	GHashTable *other_unistr_info;
 
-	/* cell metrics */
+	/* cell metrics as taken from the font, not yet scaled by cell_{width,height}_scale */
 	gint width, height, ascent;
 
 	/* reusable string for UTF-8 conversion */
@@ -399,7 +399,6 @@ font_info_measure_font (struct font_info *info)
 	 * info for them */
 	font_info_cache_ascii (info);
 
-
 	if (info->height == 0) {
 		info->height = PANGO_PIXELS_CEIL (logical.height);
 	}
@@ -412,7 +411,6 @@ font_info_measure_font (struct font_info *info)
 			  info, info->width, info->height, info->ascent);
 }
 
-
 static struct font_info *
 font_info_allocate (PangoContext *context)
 {
@@ -755,6 +753,8 @@ guint _vte_draw_get_style(gboolean bold, gboolean italic) {
 
 struct _vte_draw {
 	struct font_info *fonts[4];
+	/* cell metrics, already adjusted by cell_{width,height}_scale */
+	gint width, height, x_offset, y_offset;
 
 	cairo_t *cr;
 };
@@ -835,7 +835,9 @@ _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height
 void
 _vte_draw_set_text_font (struct _vte_draw *draw,
                          GtkWidget *widget,
-                         const PangoFontDescription *fontdesc)
+                         const PangoFontDescription *fontdesc,
+                         gdouble cell_width_scale,
+                         gdouble cell_height_scale)
 {
 	PangoFontDescription *bolddesc   = NULL;
 	PangoFontDescription *italicdesc = NULL;
@@ -895,6 +897,12 @@ _vte_draw_set_text_font (struct _vte_draw *draw,
 		font_info_destroy (draw->fonts[bold]);
 		draw->fonts[bold] = draw->fonts[normal];
 	}
+
+        /* Apply letter spacing and line spacing. */
+        draw->width = draw->fonts[VTE_DRAW_NORMAL]->width * cell_width_scale;
+        draw->x_offset = draw->fonts[VTE_DRAW_NORMAL]->width * (cell_width_scale - 1.0) / 2;
+        draw->height = draw->fonts[VTE_DRAW_NORMAL]->height * cell_height_scale;
+        draw->y_offset = draw->fonts[VTE_DRAW_NORMAL]->height * (cell_height_scale - 1.0) / 2;
 }
 
 void
@@ -904,15 +912,16 @@ _vte_draw_get_text_metrics(struct _vte_draw *draw,
 	g_return_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL);
 
 	if (width)
-		*width  = draw->fonts[VTE_DRAW_NORMAL]->width;
+                *width = draw->width;
 	if (height)
-		*height = draw->fonts[VTE_DRAW_NORMAL]->height;
+                *height = draw->height;
 	if (ascent)
-		*ascent = draw->fonts[VTE_DRAW_NORMAL]->ascent;
+                *ascent = draw->y_offset + draw->fonts[VTE_DRAW_NORMAL]->ascent;
 }
 
 
 int
+// FIXME what to do here?
 _vte_draw_get_char_width (struct _vte_draw *draw, vteunistr c, int columns,
 						  guint style)
 {
@@ -1420,15 +1429,15 @@ _vte_draw_text_internal (struct _vte_draw *draw,
 
 	for (i = 0; i < n_requests; i++) {
 		vteunistr c = requests[i].c;
-		int x = requests[i].x;
-		int y = requests[i].y + font->ascent;
+		int x = requests[i].x + draw->x_offset;
+		int y = requests[i].y + draw->y_offset + font->ascent;
 		struct unistr_info *uinfo = font_info_get_unistr_info (font, c);
 		union unistr_font_info *ufi = &uinfo->ufi;
 
                 if (_vte_draw_unichar_is_local_graphic(c)) {
                         _vte_draw_terminal_draw_graphic(draw, c, color,
                                                         requests[i].x, requests[i].y,
-                                                        font->width, requests[i].columns, font->height);
+                                                        draw->width, requests[i].columns, draw->height);
                         continue;
                 }
 
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index cc17a054..4565e460 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -62,7 +62,8 @@ void _vte_draw_clear(struct _vte_draw *draw,
 
 void _vte_draw_set_text_font(struct _vte_draw *draw,
                              GtkWidget *widget,
-			     const PangoFontDescription *fontdesc);
+			     const PangoFontDescription *fontdesc,
+			     gdouble cell_width_scale, gdouble cell_height_scale);
 void _vte_draw_get_text_metrics(struct _vte_draw *draw,
 				gint *width, gint *height, gint *ascent);
 int _vte_draw_get_char_width(struct _vte_draw *draw, vteunistr c, int columns,
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index d671957a..8a653c46 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -436,6 +436,12 @@ vte_terminal_get_property (GObject *object,
                 case PROP_BACKSPACE_BINDING:
                         g_value_set_enum (value, impl->m_backspace_binding);
                         break;
+                case PROP_CELL_HEIGHT_SCALE:
+                        g_value_set_double (value, vte_terminal_get_cell_height_scale (terminal));
+                        break;
+                case PROP_CELL_WIDTH_SCALE:
+                        g_value_set_double (value, vte_terminal_get_cell_width_scale (terminal));
+                        break;
                 case PROP_CJK_AMBIGUOUS_WIDTH:
                         g_value_set_int (value, vte_terminal_get_cjk_ambiguous_width (terminal));
                         break;
@@ -537,6 +543,12 @@ vte_terminal_set_property (GObject *object,
                 case PROP_BACKSPACE_BINDING:
                         vte_terminal_set_backspace_binding (terminal, (VteEraseBinding)g_value_get_enum (value));
                         break;
+                case PROP_CELL_HEIGHT_SCALE:
+                        vte_terminal_set_cell_height_scale (terminal, g_value_get_double (value));
+                        break;
+                case PROP_CELL_WIDTH_SCALE:
+                        vte_terminal_set_cell_width_scale (terminal, g_value_get_double (value));
+                        break;
                 case PROP_CJK_AMBIGUOUS_WIDTH:
                         vte_terminal_set_cjk_ambiguous_width (terminal, g_value_get_int (value));
                         break;
@@ -1301,6 +1313,34 @@ vte_terminal_class_init(VteTerminalClass *klass)
                                    VTE_ERASE_AUTO,
                                    (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
 
+        /**
+         * VteTerminal:cell-height-scale:
+         *
+         * Scale factor for the cell height, to increase line spacing. (The font's height is not affected.)
+         *
+         * Since: 0.52
+         */
+        pspecs[PROP_CELL_HEIGHT_SCALE] =
+                g_param_spec_double ("cell-height-scale", NULL, NULL,
+                                     VTE_CELL_SCALE_MIN,
+                                     VTE_CELL_SCALE_MAX,
+                                     1.0,
+                                     (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
+
+        /**
+         * VteTerminal:cell-width-scale:
+         *
+         * Scale factor for the cell width, to increase letter spacing. (The font's width is not affected.)
+         *
+         * Since: 0.52
+         */
+        pspecs[PROP_CELL_WIDTH_SCALE] =
+                g_param_spec_double ("cell-width-scale", NULL, NULL,
+                                     VTE_CELL_SCALE_MIN,
+                                     VTE_CELL_SCALE_MAX,
+                                     1.0,
+                                     (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
+
         /**
          * VteTerminal:cjk-ambiguous-width:
          *
@@ -3511,6 +3551,84 @@ vte_terminal_set_font_scale(VteTerminal *terminal,
                 g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_FONT_SCALE]);
 }
 
+/**
+ * vte_terminal_get_cell_height_scale:
+ * @terminal: a #VteTerminal
+ *
+ * Returns: the terminal's cell height scale
+ *
+ * Since: 0.52
+ */
+gdouble
+vte_terminal_get_cell_height_scale(VteTerminal *terminal)
+{
+        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1.);
+
+        return IMPL(terminal)->m_cell_height_scale;
+}
+
+/**
+ * vte_terminal_set_cell_height_scale:
+ * @terminal: a #VteTerminal
+ * @scale: the cell height scale
+ *
+ * Sets the terminal's cell height scale to @scale.
+ *
+ * This can be used to increase the line spacing. (The font's height is not affected.)
+ * Valid values go from 1.0 (default) to 2.0 ("double spacing").
+ *
+ * Since: 0.52
+ */
+void
+vte_terminal_set_cell_height_scale(VteTerminal *terminal,
+                                   double scale)
+{
+        g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+        scale = CLAMP(scale, VTE_CELL_SCALE_MIN, VTE_CELL_SCALE_MAX);
+        if (IMPL(terminal)->set_cell_height_scale(scale))
+                g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_CELL_HEIGHT_SCALE]);
+}
+
+/**
+ * vte_terminal_get_cell_width_scale:
+ * @terminal: a #VteTerminal
+ *
+ * Returns: the terminal's cell width scale
+ *
+ * Since: 0.52
+ */
+gdouble
+vte_terminal_get_cell_width_scale(VteTerminal *terminal)
+{
+        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1.);
+
+        return IMPL(terminal)->m_cell_width_scale;
+}
+
+/**
+ * vte_terminal_set_cell_width_scale:
+ * @terminal: a #VteTerminal
+ * @scale: the cell width scale
+ *
+ * Sets the terminal's cell width scale to @scale.
+ *
+ * This can be used to increase the letter spacing. (The font's width is not affected.)
+ * Valid values go from 1.0 (default) to 2.0.
+ *
+ * Since: 0.52
+ */
+void
+vte_terminal_set_cell_width_scale(VteTerminal *terminal,
+                                   double scale)
+{
+        g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+        scale = CLAMP(scale, VTE_CELL_SCALE_MIN, VTE_CELL_SCALE_MAX);
+        if (IMPL(terminal)->set_cell_width_scale(scale))
+                g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_CELL_WIDTH_SCALE]);
+}
+
 /* Just some arbitrary minimum values */
 #define MIN_COLUMNS (16)
 #define MIN_ROWS    (2)
diff --git a/src/vtegtk.hh b/src/vtegtk.hh
index d1ade949..ea970a4e 100644
--- a/src/vtegtk.hh
+++ b/src/vtegtk.hh
@@ -67,6 +67,8 @@ enum {
         PROP_ALLOW_HYPERLINK,
         PROP_AUDIBLE_BELL,
         PROP_BACKSPACE_BINDING,
+        PROP_CELL_HEIGHT_SCALE,
+        PROP_CELL_WIDTH_SCALE,
         PROP_CJK_AMBIGUOUS_WIDTH,
         PROP_CURSOR_BLINK_MODE,
         PROP_CURSOR_SHAPE,
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 0cd686b6..2591bf65 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -461,6 +461,8 @@ public:
         gboolean m_fontdirty;
         glong m_char_ascent;
         glong m_char_descent;
+        gdouble m_cell_width_scale;
+        gdouble m_cell_height_scale;
         /* dimensions of character cells */
         glong m_char_width;
         glong m_char_height;
@@ -1078,6 +1080,8 @@ public:
         bool set_allow_hyperlink(bool setting);
         bool set_backspace_binding(VteEraseBinding binding);
         bool set_background_alpha(double alpha);
+        bool set_cell_height_scale(double scale);
+        bool set_cell_width_scale(double scale);
         bool set_cjk_ambiguous_width(int width);
         void set_color_background(vte::color::rgb const &color);
         void set_color_bold(vte::color::rgb const& color);
