diff libinterp/corefcn/ft-text-renderer.cc @ 31607:aac27ad79be6 stable

maint: Re-indent code after switch to using namespace macros. * build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc, __ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc, __pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc, debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc, dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc, gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h, oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h, octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc, gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc, ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h, octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc, op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h: Re-indent code after switch to using namespace macros.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 18:02:15 -0800
parents e88a07dec498
children 597f3ee61a48
line wrap: on
line diff
--- a/libinterp/corefcn/ft-text-renderer.cc	Thu Dec 01 14:23:45 2022 -0800
+++ b/libinterp/corefcn/ft-text-renderer.cc	Thu Dec 01 18:02:15 2022 -0800
@@ -67,1465 +67,1465 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-  // FIXME: maybe issue at most one warning per glyph/font/size/weight
-  //        combination.
+// FIXME: maybe issue at most one warning per glyph/font/size/weight
+//        combination.
 
-  static void
-  warn_missing_glyph (FT_ULong c)
-  {
-    warning_with_id ("Octave:missing-glyph",
-                     "text_renderer: skipping missing glyph for character '%lx'", c);
-  }
+static void
+warn_missing_glyph (FT_ULong c)
+{
+  warning_with_id ("Octave:missing-glyph",
+                   "text_renderer: skipping missing glyph for character '%lx'", c);
+}
 
-  static void
-  warn_glyph_render (FT_ULong c)
-  {
-    warning_with_id ("Octave:glyph-render",
-                     "text_renderer: unable to render glyph for character '%lx'", c);
-  }
+static void
+warn_glyph_render (FT_ULong c)
+{
+  warning_with_id ("Octave:glyph-render",
+                   "text_renderer: unable to render glyph for character '%lx'", c);
+}
 
 #if defined (_MSC_VER)
-  // FIXME: is this really needed?
-  //
-  // This is just a trick to avoid multiple symbol definitions.
-  // PermMatrix.h contains a dllexport'ed Array<octave_idx_type>
-  // that will cause MSVC not to generate a new instantiation and
-  // use the imported one instead.
+// FIXME: is this really needed?
+//
+// This is just a trick to avoid multiple symbol definitions.
+// PermMatrix.h contains a dllexport'ed Array<octave_idx_type>
+// that will cause MSVC not to generate a new instantiation and
+// use the imported one instead.
 #  include "PermMatrix.h"
 #endif
 
-  // Forward declaration
-  static void ft_face_destroyed (void *object);
+// Forward declaration
+static void ft_face_destroyed (void *object);
 
-  class
-  ft_manager
-  {
-  private:
+class
+ft_manager
+{
+private:
 
-    ft_manager (void)
-      : m_library (), m_freetype_initialized (false),
-        m_fontconfig_initialized (false)
-    {
-      if (FT_Init_FreeType (&m_library))
-        error ("unable to initialize FreeType library");
-      else
-        m_freetype_initialized = true;
+  ft_manager (void)
+    : m_library (), m_freetype_initialized (false),
+      m_fontconfig_initialized (false)
+  {
+    if (FT_Init_FreeType (&m_library))
+      error ("unable to initialize FreeType library");
+    else
+      m_freetype_initialized = true;
 
 #if defined (HAVE_FONTCONFIG)
-      if (! FcInit ())
-        error ("unable to initialize fontconfig library");
-      else
-        m_fontconfig_initialized = true;
+    if (! FcInit ())
+      error ("unable to initialize fontconfig library");
+    else
+      m_fontconfig_initialized = true;
 #endif
-    }
+  }
 
-  public:
+public:
 
-    // No copying!
+  // No copying!
 
-    ft_manager (const ft_manager&) = delete;
+  ft_manager (const ft_manager&) = delete;
 
-    ft_manager& operator = (const ft_manager&) = delete;
+  ft_manager& operator = (const ft_manager&) = delete;
 
-  private:
+private:
 
-    ~ft_manager (void)
-    {
-      if (m_freetype_initialized)
-        FT_Done_FreeType (m_library);
+  ~ft_manager (void)
+  {
+    if (m_freetype_initialized)
+      FT_Done_FreeType (m_library);
 
 #if defined (HAVE_FONTCONFIG)
-      // FIXME: Skip the call to FcFini because it can trigger the assertion
-      //
-      //   octave: fccache.c:507: FcCacheFini: Assertion 'fcCacheChains[i] == ((void *)0)' failed.
-      //
-      // if (m_fontconfig_initialized)
-      //   FcFini ();
+    // FIXME: Skip the call to FcFini because it can trigger the assertion
+    //
+    //   octave: fccache.c:507: FcCacheFini: Assertion 'fcCacheChains[i] == ((void *)0)' failed.
+    //
+    // if (m_fontconfig_initialized)
+    //   FcFini ();
 #endif
-    }
+  }
 
-  public:
+public:
 
-    static bool instance_ok (void)
-    {
-      bool retval = true;
+  static bool instance_ok (void)
+  {
+    bool retval = true;
 
-      if (! m_instance)
-        {
-          m_instance = new ft_manager ();
-          singleton_cleanup_list::add (cleanup_instance);
-        }
+    if (! m_instance)
+      {
+        m_instance = new ft_manager ();
+        singleton_cleanup_list::add (cleanup_instance);
+      }
 
-      return retval;
-    }
+    return retval;
+  }
 
-    static void cleanup_instance (void)
-    { delete m_instance; m_instance = nullptr; }
+  static void cleanup_instance (void)
+  { delete m_instance; m_instance = nullptr; }
 
-    static FT_Face get_font (const std::string& name, const std::string& weight,
-                             const std::string& angle, double size,
-                             FT_ULong c = 0)
-    {
-      return (instance_ok ()
-              ? m_instance->do_get_font (name, weight, angle, size, c)
-              : nullptr);
-    }
+  static FT_Face get_font (const std::string& name, const std::string& weight,
+                           const std::string& angle, double size,
+                           FT_ULong c = 0)
+  {
+    return (instance_ok ()
+            ? m_instance->do_get_font (name, weight, angle, size, c)
+            : nullptr);
+  }
 
-    static octave_map get_system_fonts (void)
-    {
-      return (instance_ok ()
-              ? m_instance->do_get_system_fonts ()
-              : octave_map ());
-    }
+  static octave_map get_system_fonts (void)
+  {
+    return (instance_ok ()
+            ? m_instance->do_get_system_fonts ()
+            : octave_map ());
+  }
 
-    static void font_destroyed (FT_Face face)
-    {
-      if (instance_ok ())
-        m_instance->do_font_destroyed (face);
-    }
+  static void font_destroyed (FT_Face face)
+  {
+    if (instance_ok ())
+      m_instance->do_font_destroyed (face);
+  }
 
-  private:
+private:
 
-    typedef std::pair<std::string, double> ft_key;
-    typedef std::map<ft_key, FT_Face> ft_cache;
+  typedef std::pair<std::string, double> ft_key;
+  typedef std::map<ft_key, FT_Face> ft_cache;
 
-    static octave_map do_get_system_fonts (void)
-    {
-      static octave_map font_map;
+  static octave_map do_get_system_fonts (void)
+  {
+    static octave_map font_map;
 
-      if (font_map.isempty ())
-        {
+    if (font_map.isempty ())
+      {
 #if defined (HAVE_FONTCONFIG)
-          FcConfig *config = FcConfigGetCurrent();
-          FcPattern *pat = FcPatternCreate ();
-          FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SLANT, FC_WEIGHT,
-                                              FC_CHARSET, nullptr);
-          FcFontSet *fs = FcFontList (config, pat, os);
+        FcConfig *config = FcConfigGetCurrent();
+        FcPattern *pat = FcPatternCreate ();
+        FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SLANT, FC_WEIGHT,
+                                            FC_CHARSET, nullptr);
+        FcFontSet *fs = FcFontList (config, pat, os);
 
-          if (fs->nfont > 0)
-            {
-              // Mark fonts that have at least all printable ASCII chars
-              FcCharSet *minimal_charset =  FcCharSetCreate ();
-              for (int i = 32; i < 127; i++)
-                FcCharSetAddChar (minimal_charset, static_cast<FcChar32> (i));
+        if (fs->nfont > 0)
+          {
+            // Mark fonts that have at least all printable ASCII chars
+            FcCharSet *minimal_charset =  FcCharSetCreate ();
+            for (int i = 32; i < 127; i++)
+              FcCharSetAddChar (minimal_charset, static_cast<FcChar32> (i));
 
-              string_vector fields (4);
-              fields(0) = "family";
-              fields(1) = "angle";
-              fields(2) = "weight";
-              fields(3) = "suitable";
+            string_vector fields (4);
+            fields(0) = "family";
+            fields(1) = "angle";
+            fields(2) = "weight";
+            fields(3) = "suitable";
 
-              dim_vector dv (1, fs->nfont);
-              Cell families (dv);
-              Cell angles (dv);
-              Cell weights (dv);
-              Cell suitable (dv);
+            dim_vector dv (1, fs->nfont);
+            Cell families (dv);
+            Cell angles (dv);
+            Cell weights (dv);
+            Cell suitable (dv);
 
-              unsigned char *family;
-              int val;
-              for (int i = 0; fs && i < fs->nfont; i++)
-                {
-                  FcPattern *font = fs->fonts[i];
-                  if (FcPatternGetString (font, FC_FAMILY, 0, &family)
-                      == FcResultMatch)
-                    families(i) = std::string (reinterpret_cast<char *> (family));
-                  else
-                    families(i) = "unknown";
+            unsigned char *family;
+            int val;
+            for (int i = 0; fs && i < fs->nfont; i++)
+              {
+                FcPattern *font = fs->fonts[i];
+                if (FcPatternGetString (font, FC_FAMILY, 0, &family)
+                    == FcResultMatch)
+                  families(i) = std::string (reinterpret_cast<char *> (family));
+                else
+                  families(i) = "unknown";
 
-                  if (FcPatternGetInteger (font, FC_SLANT, 0, &val)
-                      == FcResultMatch)
-                    angles(i) = (val == FC_SLANT_ITALIC
-                                 || val == FC_SLANT_OBLIQUE)
-                                ? "italic" : "normal";
-                  else
-                    angles(i) = "unknown";
+                if (FcPatternGetInteger (font, FC_SLANT, 0, &val)
+                    == FcResultMatch)
+                  angles(i) = (val == FC_SLANT_ITALIC
+                               || val == FC_SLANT_OBLIQUE)
+                              ? "italic" : "normal";
+                else
+                  angles(i) = "unknown";
 
-                  if (FcPatternGetInteger (font, FC_WEIGHT, 0, &val)
-                      == FcResultMatch)
-                    weights(i) = (val == FC_WEIGHT_BOLD
-                                  || val == FC_WEIGHT_DEMIBOLD)
-                                 ? "bold" : "normal";
-                  else
-                    weights(i) = "unknown";
+                if (FcPatternGetInteger (font, FC_WEIGHT, 0, &val)
+                    == FcResultMatch)
+                  weights(i) = (val == FC_WEIGHT_BOLD
+                                || val == FC_WEIGHT_DEMIBOLD)
+                               ? "bold" : "normal";
+                else
+                  weights(i) = "unknown";
 
-                  FcCharSet *cset;
-                  if (FcPatternGetCharSet (font, FC_CHARSET, 0, &cset)
-                      == FcResultMatch)
-                    suitable(i) = (FcCharSetIsSubset (minimal_charset, cset)
-                                   ? true : false);
-                  else
-                    suitable(i) = false;
-                }
+                FcCharSet *cset;
+                if (FcPatternGetCharSet (font, FC_CHARSET, 0, &cset)
+                    == FcResultMatch)
+                  suitable(i) = (FcCharSetIsSubset (minimal_charset, cset)
+                                 ? true : false);
+                else
+                  suitable(i) = false;
+              }
 
-              font_map = octave_map (dv, fields);
+            font_map = octave_map (dv, fields);
 
-              font_map.assign ("family", families);
-              font_map.assign ("angle", angles);
-              font_map.assign ("weight", weights);
-              font_map.assign ("suitable", suitable);
+            font_map.assign ("family", families);
+            font_map.assign ("angle", angles);
+            font_map.assign ("weight", weights);
+            font_map.assign ("suitable", suitable);
 
-              if (fs)
-                FcFontSetDestroy (fs);
-            }
+            if (fs)
+              FcFontSetDestroy (fs);
+          }
 #endif
-        }
+      }
 
-      return font_map;
-    }
+    return font_map;
+  }
 
-    FT_Face do_get_font (const std::string& name, const std::string& weight,
-                         const std::string& angle, double size,
-                         FT_ULong search_code_point)
-    {
-      FT_Face retval = nullptr;
+  FT_Face do_get_font (const std::string& name, const std::string& weight,
+                       const std::string& angle, double size,
+                       FT_ULong search_code_point)
+  {
+    FT_Face retval = nullptr;
 
 #if defined (HAVE_FT_REFERENCE_FACE)
-      // Look first into the font cache, then use fontconfig.  If the font
-      // is present in the cache, simply add a reference and return it.
-
-      ft_key key (name + ':' + weight + ':' + angle + ':'
-                  + std::to_string (search_code_point), size);
-      ft_cache::const_iterator it = m_cache.find (key);
-
-      if (it != m_cache.end ())
-        {
-          FT_Reference_Face (it->second);
-          return it->second;
-        }
-#endif
-
-      static std::string fonts_dir;
-
-      if (fonts_dir.empty ())
-        {
-          fonts_dir = sys::env::getenv ("OCTAVE_FONTS_DIR");
-
-          if (fonts_dir.empty ())
-#if defined (SYSTEM_FREEFONT_DIR)
-            fonts_dir = SYSTEM_FREEFONT_DIR;
-#else
-            fonts_dir = config::oct_fonts_dir ();
-#endif
-        }
-
-
-      // Default font file
-      std::string file;
-
-      if (! fonts_dir.empty ())
-        {
-          file = fonts_dir + sys::file_ops::dir_sep_str () + "FreeSans";
-
-          if (weight == "bold")
-            file += "Bold";
-
-          if (angle == "italic" || angle == "oblique")
-            file += "Oblique";
-
-          file += ".otf";
-        }
-
-#if defined (HAVE_FONTCONFIG)
-      if ((search_code_point != 0 || name != "*") && m_fontconfig_initialized)
-        {
-          int fc_weight, fc_angle;
-
-          if (weight == "bold")
-            fc_weight = FC_WEIGHT_BOLD;
-          else
-            fc_weight = FC_WEIGHT_NORMAL;
+    // Look first into the font cache, then use fontconfig.  If the font
+    // is present in the cache, simply add a reference and return it.
 
-          if (angle == "italic")
-            fc_angle = FC_SLANT_ITALIC;
-          else if (angle == "oblique")
-            fc_angle = FC_SLANT_OBLIQUE;
-          else
-            fc_angle = FC_SLANT_ROMAN;
-
-          FcPattern *pat = FcPatternCreate ();
-
-          FcPatternAddString (pat, FC_FAMILY,
-                              (reinterpret_cast<const FcChar8 *>
-                               (name.c_str ())));
-
-          FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
-          FcPatternAddInteger (pat, FC_SLANT, fc_angle);
-          FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
-
-          if (search_code_point > 0)
-            {
-              FcCharSet *minimal_charset =  FcCharSetCreate ();
-              FcCharSetAddChar (minimal_charset,
-                                static_cast<FcChar32> (search_code_point));
-              FcPatternAddCharSet (pat, FC_CHARSET, minimal_charset);
-            }
+    ft_key key (name + ':' + weight + ':' + angle + ':'
+                + std::to_string (search_code_point), size);
+    ft_cache::const_iterator it = m_cache.find (key);
 
-          if (FcConfigSubstitute (nullptr, pat, FcMatchPattern))
-            {
-              FcResult res;
-              FcPattern *match;
-
-              FcDefaultSubstitute (pat);
-
-              match = FcFontMatch (nullptr, pat, &res);
-
-              // FIXME: originally, this test also required that
-              // res != FcResultNoMatch.  Is that really needed?
-              if (match)
-                {
-                  unsigned char *tmp;
-
-                  FcPatternGetString (match, FC_FILE, 0, &tmp);
-                  file = reinterpret_cast<char *> (tmp);
-                }
-              else
-                ::warning ("could not match any font: %s-%s-%s-%g, using default font",
-                           name.c_str (), weight.c_str (), angle.c_str (),
-                           size);
-
-              if (match)
-                FcPatternDestroy (match);
-            }
-
-          FcPatternDestroy (pat);
-        }
+    if (it != m_cache.end ())
+      {
+        FT_Reference_Face (it->second);
+        return it->second;
+      }
 #endif
 
-      if (file.empty ())
-        ::warning ("unable to find default font files");
-      else
-        {
-          std::string ascii_file = sys::get_ASCII_filename (file);
-
-          if (FT_New_Face (m_library, ascii_file.c_str (), 0, &retval))
-            ::warning ("ft_manager: unable to load font: %s", file.c_str ());
-#if defined (HAVE_FT_REFERENCE_FACE)
-          else
-            {
-              // Install a finalizer to notify ft_manager that the font is
-              // being destroyed.  The class ft_manager only keeps weak
-              // references to font objects.
+    static std::string fonts_dir;
 
-              retval->generic.data = new ft_key (key);
-              retval->generic.finalizer = ft_face_destroyed;
-
-              // Insert loaded font into the cache.
-              if (FT_Reference_Face (retval) == 0)
-                m_cache[key] = retval;
-            }
-#endif
-        }
-
-      return retval;
-    }
-
-    void do_font_destroyed (FT_Face face)
-    {
-      if (face->generic.data)
-        {
-          ft_key *pkey = reinterpret_cast<ft_key *> (face->generic.data);
+    if (fonts_dir.empty ())
+      {
+        fonts_dir = sys::env::getenv ("OCTAVE_FONTS_DIR");
 
-          m_cache.erase (*pkey);
-          delete pkey;
-          face->generic.data = nullptr;
-          FT_Done_Face (face);
-        }
-    }
-
-    //--------
-
-    static ft_manager *m_instance;
-
-    // Cache the fonts loaded by FreeType.  This cache only contains
-    // weak references to the fonts, strong references are only present
-    // in class text_renderer.
-    ft_cache m_cache;
-
-    FT_Library m_library;
-    bool m_freetype_initialized;
-    bool m_fontconfig_initialized;
-  };
+        if (fonts_dir.empty ())
+#if defined (SYSTEM_FREEFONT_DIR)
+          fonts_dir = SYSTEM_FREEFONT_DIR;
+#else
+          fonts_dir = config::oct_fonts_dir ();
+#endif
+      }
 
-  ft_manager *ft_manager::m_instance = nullptr;
-
-  static void
-  ft_face_destroyed (void *object)
-  {
-    ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object));
-  }
-
-  class
-  OCTINTERP_API
-  ft_text_renderer : public base_text_renderer
-  {
-  public:
-
-    enum
-    {
-      MODE_BBOX   = 0,
-      MODE_RENDER = 1
-    };
-
-  public:
 
-    ft_text_renderer (void)
-      : base_text_renderer (), m_font (), m_bbox (1, 4, 0.0), m_halign (0),
-        m_xoffset (0), m_line_yoffset (0), m_yoffset (0), m_mode (MODE_BBOX),
-        m_color (dim_vector (1, 3), 0), m_do_strlist (false), m_strlist (),
-        m_line_xoffset (0), m_ymin (0), m_ymax (0), m_deltax (0),
-        m_max_fontsize (0), m_antialias (true)
-    { }
-
-    // No copying!
-
-    ft_text_renderer (const ft_text_renderer&) = delete;
-
-    ft_text_renderer& operator = (const ft_text_renderer&) = delete;
-
-    ~ft_text_renderer (void) = default;
-
-    void visit (text_element_string& e);
-
-    void visit (text_element_list& e);
-
-    void visit (text_element_subscript& e);
+    // Default font file
+    std::string file;
 
-    void visit (text_element_superscript& e);
-
-    void visit (text_element_color& e);
-
-    void visit (text_element_fontsize& e);
-
-    void visit (text_element_fontname& e);
-
-    void visit (text_element_fontstyle& e);
-
-    void visit (text_element_symbol& e);
-
-    void visit (text_element_combined& e);
-
-    void reset (void);
-
-    uint8NDArray get_pixels (void) const { return m_pixels; }
-
-    Matrix get_boundingbox (void) const { return m_bbox; }
-
-    uint8NDArray render (text_element *elt, Matrix& box,
-                         int rotation = ROTATION_0);
+    if (! fonts_dir.empty ())
+      {
+        file = fonts_dir + sys::file_ops::dir_sep_str () + "FreeSans";
 
-    Matrix get_extent (text_element *elt, double rotation = 0.0);
-    Matrix get_extent (const std::string& txt, double rotation,
-                       const caseless_str& interpreter);
-
-    void set_anti_aliasing (bool val) { m_antialias = val; };
-
-    void set_font (const std::string& name, const std::string& weight,
-                   const std::string& angle, double size);
-
-    octave_map get_system_fonts (void);
-
-    void set_color (const Matrix& c);
-
-    void set_mode (int m);
-
-    void text_to_pixels (const std::string& txt,
-                         uint8NDArray& pxls, Matrix& bbox,
-                         int halign, int valign, double rotation,
-                         const caseless_str& interpreter,
-                         bool handle_rotation);
+        if (weight == "bold")
+          file += "Bold";
 
-  private:
-
-    // Class to hold information about fonts and a strong
-    // reference to the font objects loaded by FreeType.
-
-    class ft_font : public text_renderer::font
-    {
-    public:
-
-      ft_font (void)
-        : text_renderer::font (), m_face (nullptr) { }
+        if (angle == "italic" || angle == "oblique")
+          file += "Oblique";
 
-      ft_font (const std::string& nm, const std::string& wt,
-               const std::string& ang, double sz, FT_Face f = nullptr)
-        : text_renderer::font (nm, wt, ang, sz), m_face (f)
-      { }
-
-      ft_font (const ft_font& ft);
-
-      ~ft_font (void)
-      {
-        if (m_face)
-          FT_Done_Face (m_face);
+        file += ".otf";
       }
 
-      ft_font& operator = (const ft_font& ft);
-
-      bool is_valid (void) const { return get_face (); }
-
-      FT_Face get_face (void) const;
-
-    private:
-
-      mutable FT_Face m_face;
-    };
+#if defined (HAVE_FONTCONFIG)
+    if ((search_code_point != 0 || name != "*") && m_fontconfig_initialized)
+      {
+        int fc_weight, fc_angle;
 
-    void push_new_line (void);
-
-    void update_line_bbox (void);
-
-    void compute_bbox (void);
+        if (weight == "bold")
+          fc_weight = FC_WEIGHT_BOLD;
+        else
+          fc_weight = FC_WEIGHT_NORMAL;
 
-    int compute_line_xoffset (const Matrix& lb) const;
+        if (angle == "italic")
+          fc_angle = FC_SLANT_ITALIC;
+        else if (angle == "oblique")
+          fc_angle = FC_SLANT_OBLIQUE;
+        else
+          fc_angle = FC_SLANT_ROMAN;
 
-    FT_UInt process_character (FT_ULong code, FT_UInt previous,
-                               std::string& sub_font);
-
-  public:
+        FcPattern *pat = FcPatternCreate ();
 
-    void text_to_strlist (const std::string& txt,
-                          std::list<text_renderer::string>& lst, Matrix& bbox,
-                          int halign, int valign, double rotation,
-                          const caseless_str& interp);
+        FcPatternAddString (pat, FC_FAMILY,
+                            (reinterpret_cast<const FcChar8 *>
+                             (name.c_str ())));
 
-  private:
-
-    // The current font used by the renderer.
-    ft_font m_font;
+        FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
+        FcPatternAddInteger (pat, FC_SLANT, fc_angle);
+        FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
 
-    // Used to stored the bounding box corresponding to the rendered text.
-    // The bounding box has the form [x, y, w, h] where x and y represent the
-    // coordinates of the bottom left corner relative to the anchor point of
-    // the text (== start of text on the baseline).  Due to font descent or
-    // multiple lines, the value y is usually negative.
-    Matrix m_bbox;
+        if (search_code_point > 0)
+          {
+            FcCharSet *minimal_charset =  FcCharSetCreate ();
+            FcCharSetAddChar (minimal_charset,
+                              static_cast<FcChar32> (search_code_point));
+            FcPatternAddCharSet (pat, FC_CHARSET, minimal_charset);
+          }
 
-    // Used to stored the rendered text.  It's a 3D matrix with size MxNx4
-    // where M and N are the width and height of the bounding box.
-    uint8NDArray m_pixels;
+        if (FcConfigSubstitute (nullptr, pat, FcMatchPattern))
+          {
+            FcResult res;
+            FcPattern *match;
 
-    // Used to store the bounding box of each line.  This is used to layout
-    // multiline text properly.
-    std::list<Matrix> m_line_bbox;
+            FcDefaultSubstitute (pat);
 
-    // The current horizontal alignment.  This is used to align multi-line text.
-    int m_halign;
+            match = FcFontMatch (nullptr, pat, &res);
 
-    // The X offset for the next glyph.
-    int m_xoffset;
-
-    // The Y offset of the baseline for the current line.
-    int m_line_yoffset;
-
-    // The Y offset of the baseline for the next glyph.  The offset is relative
-    // to line_yoffset.  The total Y offset is computed with:
-    // line_yoffset + yoffset.
-    int m_yoffset;
+            // FIXME: originally, this test also required that
+            // res != FcResultNoMatch.  Is that really needed?
+            if (match)
+              {
+                unsigned char *tmp;
 
-    // The current mode of the rendering process (box computing or rendering).
-    int m_mode;
-
-    // The base color of the rendered text.
-    uint8NDArray m_color;
+                FcPatternGetString (match, FC_FILE, 0, &tmp);
+                file = reinterpret_cast<char *> (tmp);
+              }
+            else
+              ::warning ("could not match any font: %s-%s-%s-%g, using default font",
+                         name.c_str (), weight.c_str (), angle.c_str (),
+                         size);
 
-    // A list of parsed strings to be used for printing.
-    bool m_do_strlist;
-    std::list<text_renderer::string> m_strlist;
+            if (match)
+              FcPatternDestroy (match);
+          }
 
-    // The X offset of the baseline for the current line.
-    int m_line_xoffset;
+        FcPatternDestroy (pat);
+      }
+#endif
 
-    // Min and max y coordinates of all glyphs in a line.
-    FT_Pos m_ymin;
-    FT_Pos m_ymax;
-
-    // Difference between the advance and the actual extent of the latest glyph
-    FT_Pos m_deltax;
-
-    // Used for computing the distance between lines.
-    double m_max_fontsize;
-
-    // Anti-aliasing.
-    bool m_antialias;
+    if (file.empty ())
+      ::warning ("unable to find default font files");
+    else
+      {
+        std::string ascii_file = sys::get_ASCII_filename (file);
 
-  };
+        if (FT_New_Face (m_library, ascii_file.c_str (), 0, &retval))
+          ::warning ("ft_manager: unable to load font: %s", file.c_str ());
+#if defined (HAVE_FT_REFERENCE_FACE)
+        else
+          {
+            // Install a finalizer to notify ft_manager that the font is
+            // being destroyed.  The class ft_manager only keeps weak
+            // references to font objects.
 
-  void
-  ft_text_renderer::set_font (const std::string& name,
-                              const std::string& weight,
-                              const std::string& angle, double size)
-  {
-    // FIXME: take "fontunits" into account
-    m_font = ft_font (name, weight, angle, size, nullptr);
-  }
+            retval->generic.data = new ft_key (key);
+            retval->generic.finalizer = ft_face_destroyed;
 
-  octave_map
-  ft_text_renderer::get_system_fonts (void)
-  {
-    return ft_manager::get_system_fonts ();
+            // Insert loaded font into the cache.
+            if (FT_Reference_Face (retval) == 0)
+              m_cache[key] = retval;
+          }
+#endif
+      }
+
+    return retval;
   }
 
-  void
-  ft_text_renderer::push_new_line (void)
+  void do_font_destroyed (FT_Face face)
   {
-    switch (m_mode)
-      {
-      case MODE_BBOX:
-        {
-          // Create a new bbox entry based on the current font.
-
-          FT_Face face = m_font.get_face ();
-
-          if (face)
-            {
-              Matrix bb (1, 5, 0.0);
-
-              m_line_bbox.push_back (bb);
-
-              m_xoffset = m_yoffset = 0;
-              m_ymin = m_ymax = m_deltax = 0;
-            }
-        }
-        break;
-
-      case MODE_RENDER:
-        {
-          // Move to the next line bbox, adjust xoffset based on alignment
-          // and yoffset based on the old and new line bbox.
-
-          Matrix old_bbox = m_line_bbox.front ();
-          m_line_bbox.pop_front ();
-          Matrix new_bbox = m_line_bbox.front ();
-
-          m_xoffset = m_line_xoffset = compute_line_xoffset (new_bbox);
-          m_line_yoffset -= (-old_bbox(1) + math::round (0.4 * m_max_fontsize)
-                             + (new_bbox(3) + new_bbox(1)));
-          m_yoffset = 0;
-          m_ymin = m_ymax = m_deltax = 0;
-        }
-        break;
-      }
-  }
-
-  int
-  ft_text_renderer::compute_line_xoffset (const Matrix& lb) const
-  {
-    if (! m_bbox.isempty ())
+    if (face->generic.data)
       {
-        switch (m_halign)
-          {
-          case 0:
-            return 0;
-          case 1:
-            return (m_bbox(2) - lb(2)) / 2;
-          case 2:
-            return (m_bbox(2) - lb(2));
-          }
-      }
-
-    return 0;
-  }
-
-  void
-  ft_text_renderer::compute_bbox (void)
-  {
-    // Stack the various line bbox together and compute the final
-    // bounding box for the entire text string.
-
-    m_bbox = Matrix ();
-
-    switch (m_line_bbox.size ())
-      {
-      case 0:
-        break;
-
-      case 1:
-        m_bbox = m_line_bbox.front ().extract (0, 0, 0, 3);
-        break;
+        ft_key *pkey = reinterpret_cast<ft_key *> (face->generic.data);
 
-      default:
-        for (const auto& lbox : m_line_bbox)
-          {
-            if (m_bbox.isempty ())
-              m_bbox = lbox.extract (0, 0, 0, 3);
-            else
-              {
-                double delta = math::round (0.4 * m_max_fontsize) + lbox(3);
-                m_bbox(1) -= delta;
-                m_bbox(3) += delta;
-                m_bbox(2) = math::max (m_bbox(2), lbox(2));
-              }
-          }
-        break;
-      }
-  }
-
-  void
-  ft_text_renderer::update_line_bbox (void)
-  {
-    // Called after a font change, when in MODE_BBOX mode, to update the
-    // current line bbox with the new font metrics.  This also includes the
-    // current yoffset, that is the offset of the current glyph's baseline
-    // the line's baseline.
-
-    if (m_mode == MODE_BBOX)
-      {
-        Matrix& bb = m_line_bbox.back ();
-        bb(1) = m_ymin;
-        // Add one pixel to the bbox height to avoid occasional text clipping.
-        // See bug #55328.
-        bb(3) = (m_ymax + 1) - m_ymin;
-        if (m_deltax > 0)
-          bb(2) += m_deltax;
+        m_cache.erase (*pkey);
+        delete pkey;
+        face->generic.data = nullptr;
+        FT_Done_Face (face);
       }
   }
 
-  void
-  ft_text_renderer::set_mode (int m)
+  //--------
+
+  static ft_manager *m_instance;
+
+  // Cache the fonts loaded by FreeType.  This cache only contains
+  // weak references to the fonts, strong references are only present
+  // in class text_renderer.
+  ft_cache m_cache;
+
+  FT_Library m_library;
+  bool m_freetype_initialized;
+  bool m_fontconfig_initialized;
+};
+
+ft_manager *ft_manager::m_instance = nullptr;
+
+static void
+ft_face_destroyed (void *object)
+{
+  ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object));
+}
+
+class
+OCTINTERP_API
+ft_text_renderer : public base_text_renderer
+{
+public:
+
+  enum
+  {
+    MODE_BBOX   = 0,
+    MODE_RENDER = 1
+  };
+
+public:
+
+  ft_text_renderer (void)
+    : base_text_renderer (), m_font (), m_bbox (1, 4, 0.0), m_halign (0),
+      m_xoffset (0), m_line_yoffset (0), m_yoffset (0), m_mode (MODE_BBOX),
+      m_color (dim_vector (1, 3), 0), m_do_strlist (false), m_strlist (),
+      m_line_xoffset (0), m_ymin (0), m_ymax (0), m_deltax (0),
+      m_max_fontsize (0), m_antialias (true)
+  { }
+
+  // No copying!
+
+  ft_text_renderer (const ft_text_renderer&) = delete;
+
+  ft_text_renderer& operator = (const ft_text_renderer&) = delete;
+
+  ~ft_text_renderer (void) = default;
+
+  void visit (text_element_string& e);
+
+  void visit (text_element_list& e);
+
+  void visit (text_element_subscript& e);
+
+  void visit (text_element_superscript& e);
+
+  void visit (text_element_color& e);
+
+  void visit (text_element_fontsize& e);
+
+  void visit (text_element_fontname& e);
+
+  void visit (text_element_fontstyle& e);
+
+  void visit (text_element_symbol& e);
+
+  void visit (text_element_combined& e);
+
+  void reset (void);
+
+  uint8NDArray get_pixels (void) const { return m_pixels; }
+
+  Matrix get_boundingbox (void) const { return m_bbox; }
+
+  uint8NDArray render (text_element *elt, Matrix& box,
+                       int rotation = ROTATION_0);
+
+  Matrix get_extent (text_element *elt, double rotation = 0.0);
+  Matrix get_extent (const std::string& txt, double rotation,
+                     const caseless_str& interpreter);
+
+  void set_anti_aliasing (bool val) { m_antialias = val; };
+
+  void set_font (const std::string& name, const std::string& weight,
+                 const std::string& angle, double size);
+
+  octave_map get_system_fonts (void);
+
+  void set_color (const Matrix& c);
+
+  void set_mode (int m);
+
+  void text_to_pixels (const std::string& txt,
+                       uint8NDArray& pxls, Matrix& bbox,
+                       int halign, int valign, double rotation,
+                       const caseless_str& interpreter,
+                       bool handle_rotation);
+
+private:
+
+  // Class to hold information about fonts and a strong
+  // reference to the font objects loaded by FreeType.
+
+  class ft_font : public text_renderer::font
   {
-    m_mode = m;
+  public:
+
+    ft_font (void)
+      : text_renderer::font (), m_face (nullptr) { }
+
+    ft_font (const std::string& nm, const std::string& wt,
+             const std::string& ang, double sz, FT_Face f = nullptr)
+      : text_renderer::font (nm, wt, ang, sz), m_face (f)
+    { }
+
+    ft_font (const ft_font& ft);
+
+    ~ft_font (void)
+    {
+      if (m_face)
+        FT_Done_Face (m_face);
+    }
+
+    ft_font& operator = (const ft_font& ft);
+
+    bool is_valid (void) const { return get_face (); }
+
+    FT_Face get_face (void) const;
+
+  private:
+
+    mutable FT_Face m_face;
+  };
+
+  void push_new_line (void);
+
+  void update_line_bbox (void);
+
+  void compute_bbox (void);
+
+  int compute_line_xoffset (const Matrix& lb) const;
+
+  FT_UInt process_character (FT_ULong code, FT_UInt previous,
+                             std::string& sub_font);
+
+public:
+
+  void text_to_strlist (const std::string& txt,
+                        std::list<text_renderer::string>& lst, Matrix& bbox,
+                        int halign, int valign, double rotation,
+                        const caseless_str& interp);
+
+private:
+
+  // The current font used by the renderer.
+  ft_font m_font;
+
+  // Used to stored the bounding box corresponding to the rendered text.
+  // The bounding box has the form [x, y, w, h] where x and y represent the
+  // coordinates of the bottom left corner relative to the anchor point of
+  // the text (== start of text on the baseline).  Due to font descent or
+  // multiple lines, the value y is usually negative.
+  Matrix m_bbox;
+
+  // Used to stored the rendered text.  It's a 3D matrix with size MxNx4
+  // where M and N are the width and height of the bounding box.
+  uint8NDArray m_pixels;
 
-    switch (m_mode)
+  // Used to store the bounding box of each line.  This is used to layout
+  // multiline text properly.
+  std::list<Matrix> m_line_bbox;
+
+  // The current horizontal alignment.  This is used to align multi-line text.
+  int m_halign;
+
+  // The X offset for the next glyph.
+  int m_xoffset;
+
+  // The Y offset of the baseline for the current line.
+  int m_line_yoffset;
+
+  // The Y offset of the baseline for the next glyph.  The offset is relative
+  // to line_yoffset.  The total Y offset is computed with:
+  // line_yoffset + yoffset.
+  int m_yoffset;
+
+  // The current mode of the rendering process (box computing or rendering).
+  int m_mode;
+
+  // The base color of the rendered text.
+  uint8NDArray m_color;
+
+  // A list of parsed strings to be used for printing.
+  bool m_do_strlist;
+  std::list<text_renderer::string> m_strlist;
+
+  // The X offset of the baseline for the current line.
+  int m_line_xoffset;
+
+  // Min and max y coordinates of all glyphs in a line.
+  FT_Pos m_ymin;
+  FT_Pos m_ymax;
+
+  // Difference between the advance and the actual extent of the latest glyph
+  FT_Pos m_deltax;
+
+  // Used for computing the distance between lines.
+  double m_max_fontsize;
+
+  // Anti-aliasing.
+  bool m_antialias;
+
+};
+
+void
+ft_text_renderer::set_font (const std::string& name,
+                            const std::string& weight,
+                            const std::string& angle, double size)
+{
+  // FIXME: take "fontunits" into account
+  m_font = ft_font (name, weight, angle, size, nullptr);
+}
+
+octave_map
+ft_text_renderer::get_system_fonts (void)
+{
+  return ft_manager::get_system_fonts ();
+}
+
+void
+ft_text_renderer::push_new_line (void)
+{
+  switch (m_mode)
+    {
+    case MODE_BBOX:
       {
-      case MODE_BBOX:
-        m_xoffset = m_line_yoffset = m_yoffset = 0;
-        m_max_fontsize = 0.0;
-        m_bbox = Matrix (1, 4, 0.0);
-        m_line_bbox.clear ();
-        push_new_line ();
-        break;
+        // Create a new bbox entry based on the current font.
+
+        FT_Face face = m_font.get_face ();
 
-      case MODE_RENDER:
-        if (m_bbox.numel () != 4)
+        if (face)
           {
-            ::error ("ft_text_renderer: invalid bounding box, cannot render");
+            Matrix bb (1, 5, 0.0);
 
-            m_xoffset = m_line_yoffset = m_yoffset = 0;
-            m_pixels = uint8NDArray ();
+            m_line_bbox.push_back (bb);
+
+            m_xoffset = m_yoffset = 0;
+            m_ymin = m_ymax = m_deltax = 0;
           }
-        else
-          {
-            dim_vector d (4, octave_idx_type (m_bbox(2)),
-                          octave_idx_type (m_bbox(3)));
-            m_pixels = uint8NDArray (d, static_cast<uint8_t> (0));
-            m_xoffset = compute_line_xoffset (m_line_bbox.front ());
-            m_line_yoffset = -m_bbox(1);
-            m_yoffset = 0;
-          }
-        break;
+      }
+      break;
+
+    case MODE_RENDER:
+      {
+        // Move to the next line bbox, adjust xoffset based on alignment
+        // and yoffset based on the old and new line bbox.
 
-      default:
-        error ("ft_text_renderer: invalid mode '%d'", m_mode);
-        break;
-      }
-  }
+        Matrix old_bbox = m_line_bbox.front ();
+        m_line_bbox.pop_front ();
+        Matrix new_bbox = m_line_bbox.front ();
 
-  bool is_opaque (const FT_GlyphSlot& glyph, const int x, const int y)
-  {
-    // Borrowed from https://stackoverflow.com/questions/14800827/
-    //    indexing-pixels-in-a-monochrome-freetype-glyph-buffer
-    int pitch = std::abs (glyph->bitmap.pitch);
-    unsigned char *row = &glyph->bitmap.buffer[pitch * y];
-    char cvalue = row[x >> 3];
+        m_xoffset = m_line_xoffset = compute_line_xoffset (new_bbox);
+        m_line_yoffset -= (-old_bbox(1) + math::round (0.4 * m_max_fontsize)
+                           + (new_bbox(3) + new_bbox(1)));
+        m_yoffset = 0;
+        m_ymin = m_ymax = m_deltax = 0;
+      }
+      break;
+    }
+}
 
-    return ((cvalue & (128 >> (x & 7))) != 0);
-  }
+int
+ft_text_renderer::compute_line_xoffset (const Matrix& lb) const
+{
+  if (! m_bbox.isempty ())
+    {
+      switch (m_halign)
+        {
+        case 0:
+          return 0;
+        case 1:
+          return (m_bbox(2) - lb(2)) / 2;
+        case 2:
+          return (m_bbox(2) - lb(2));
+        }
+    }
+
+  return 0;
+}
+
+void
+ft_text_renderer::compute_bbox (void)
+{
+  // Stack the various line bbox together and compute the final
+  // bounding box for the entire text string.
+
+  m_bbox = Matrix ();
 
-  FT_UInt
-  ft_text_renderer::process_character (FT_ULong code, FT_UInt previous,
-                                       std::string& sub_name)
-  {
-    FT_Face face = m_font.get_face ();
+  switch (m_line_bbox.size ())
+    {
+    case 0:
+      break;
+
+    case 1:
+      m_bbox = m_line_bbox.front ().extract (0, 0, 0, 3);
+      break;
 
-    sub_name = face->family_name;
+    default:
+      for (const auto& lbox : m_line_bbox)
+        {
+          if (m_bbox.isempty ())
+            m_bbox = lbox.extract (0, 0, 0, 3);
+          else
+            {
+              double delta = math::round (0.4 * m_max_fontsize) + lbox(3);
+              m_bbox(1) -= delta;
+              m_bbox(3) += delta;
+              m_bbox(2) = math::max (m_bbox(2), lbox(2));
+            }
+        }
+      break;
+    }
+}
+
+void
+ft_text_renderer::update_line_bbox (void)
+{
+  // Called after a font change, when in MODE_BBOX mode, to update the
+  // current line bbox with the new font metrics.  This also includes the
+  // current yoffset, that is the offset of the current glyph's baseline
+  // the line's baseline.
 
-    FT_UInt glyph_index = 0;
+  if (m_mode == MODE_BBOX)
+    {
+      Matrix& bb = m_line_bbox.back ();
+      bb(1) = m_ymin;
+      // Add one pixel to the bbox height to avoid occasional text clipping.
+      // See bug #55328.
+      bb(3) = (m_ymax + 1) - m_ymin;
+      if (m_deltax > 0)
+        bb(2) += m_deltax;
+    }
+}
+
+void
+ft_text_renderer::set_mode (int m)
+{
+  m_mode = m;
+
+  switch (m_mode)
+    {
+    case MODE_BBOX:
+      m_xoffset = m_line_yoffset = m_yoffset = 0;
+      m_max_fontsize = 0.0;
+      m_bbox = Matrix (1, 4, 0.0);
+      m_line_bbox.clear ();
+      push_new_line ();
+      break;
+
+    case MODE_RENDER:
+      if (m_bbox.numel () != 4)
+        {
+          ::error ("ft_text_renderer: invalid bounding box, cannot render");
 
-    if (face)
-      {
-        glyph_index = FT_Get_Char_Index (face, code);
+          m_xoffset = m_line_yoffset = m_yoffset = 0;
+          m_pixels = uint8NDArray ();
+        }
+      else
+        {
+          dim_vector d (4, octave_idx_type (m_bbox(2)),
+                        octave_idx_type (m_bbox(3)));
+          m_pixels = uint8NDArray (d, static_cast<uint8_t> (0));
+          m_xoffset = compute_line_xoffset (m_line_bbox.front ());
+          m_line_yoffset = -m_bbox(1);
+          m_yoffset = 0;
+        }
+      break;
+
+    default:
+      error ("ft_text_renderer: invalid mode '%d'", m_mode);
+      break;
+    }
+}
 
-        if (code != '\n' && code != '\t'
-            && (! glyph_index
-                || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
-          {
+bool is_opaque (const FT_GlyphSlot& glyph, const int x, const int y)
+{
+  // Borrowed from https://stackoverflow.com/questions/14800827/
+                         //    indexing-pixels-in-a-monochrome-freetype-glyph-buffer
+  int pitch = std::abs (glyph->bitmap.pitch);
+  unsigned char *row = &glyph->bitmap.buffer[pitch * y];
+  char cvalue = row[x >> 3];
+
+  return ((cvalue & (128 >> (x & 7))) != 0);
+}
+
+FT_UInt
+ft_text_renderer::process_character (FT_ULong code, FT_UInt previous,
+                                     std::string& sub_name)
+{
+  FT_Face face = m_font.get_face ();
+
+  sub_name = face->family_name;
+
+  FT_UInt glyph_index = 0;
+
+  if (face)
+    {
+      glyph_index = FT_Get_Char_Index (face, code);
+
+      if (code != '\n' && code != '\t'
+          && (! glyph_index
+              || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
+        {
 #if defined (HAVE_FONTCONFIG)
-            // Try to substitue font
-            FT_Face sub_face = ft_manager::get_font (m_font.get_name (),
-                                                     m_font.get_weight (),
-                                                     m_font.get_angle (),
-                                                     m_font.get_size (),
-                                                     code);
+          // Try to substitue font
+          FT_Face sub_face = ft_manager::get_font (m_font.get_name (),
+                             m_font.get_weight (),
+                             m_font.get_angle (),
+                             m_font.get_size (),
+                             code);
+
+          if (sub_face)
+            {
+              FT_Set_Char_Size (sub_face, 0, m_font.get_size ()*64, 0, 0);
+
+              glyph_index = FT_Get_Char_Index (sub_face, code);
 
-            if (sub_face)
-              {
-                FT_Set_Char_Size (sub_face, 0, m_font.get_size ()*64, 0, 0);
+              if (glyph_index
+                  && (FT_Load_Glyph (sub_face, glyph_index, FT_LOAD_DEFAULT)
+                      == 0))
+                {
+                  static std::string prev_sub_name;
 
-                glyph_index = FT_Get_Char_Index (sub_face, code);
+                  if (prev_sub_name.empty ()
+                      || prev_sub_name != std::string (sub_face->family_name))
+                    {
+                      prev_sub_name = sub_face->family_name;
+                      warning_with_id ("Octave:substituted-glyph",
+                                       "text_renderer: substituting font to '%s' for some characters",
+                                       sub_face->family_name);
+                    }
+
+                  ft_font saved_font = m_font;
+
+                  m_font = ft_font (m_font.get_name (), m_font.get_weight (),
+                                    m_font.get_angle (), m_font.get_size (),
+                                    sub_face);
+
+                  process_character (code, previous, sub_name);
 
-                if (glyph_index
-                    && (FT_Load_Glyph (sub_face, glyph_index, FT_LOAD_DEFAULT)
-                        == 0))
-                  {
-                    static std::string prev_sub_name;
+                  m_font = saved_font;
+                }
+              else
+                {
+                  glyph_index = 0;
+                  warn_missing_glyph (code);
+                }
+            }
+          else
+            {
+              glyph_index = 0;
+              warn_missing_glyph (code);
+            }
+#else
+          glyph_index = 0;
+          warn_missing_glyph (code);
+#endif
+        }
+      else if ((code == '\n') || (code == '\t'))
+        {
+          glyph_index = FT_Get_Char_Index (face, ' ');
+          if (! glyph_index
+              || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
+            {
+              glyph_index = 0;
+              warn_missing_glyph (' ');
+            }
+          else if (code == '\n')
+            push_new_line ();
+          else
+            {
+              // Advance to next multiple of 4 times the width of the "space"
+              // character.
+              int x_tab = 4 * (face->glyph->advance.x >> 6);
+              m_xoffset = (1 + std::floor (1. * m_xoffset / x_tab)) * x_tab;
+            }
+        }
+      else
+        {
+          switch (m_mode)
+            {
+            case MODE_RENDER:
+              if (FT_Render_Glyph (face->glyph, (m_antialias
+                                                 ? FT_RENDER_MODE_NORMAL
+                                                 : FT_RENDER_MODE_MONO)))
+                {
+                  glyph_index = 0;
+                  warn_glyph_render (code);
+                }
+              else
+                {
+                  FT_Bitmap& bitmap = face->glyph->bitmap;
+                  int x0, y0;
 
-                    if (prev_sub_name.empty ()
-                        || prev_sub_name != std::string (sub_face->family_name))
+                  if (previous)
+                    {
+                      FT_Vector delta;
+
+                      FT_Get_Kerning (face, previous, glyph_index,
+                                      FT_KERNING_DEFAULT, &delta);
+
+                      m_xoffset += (delta.x >> 6);
+                    }
+
+                  x0 = m_xoffset + face->glyph->bitmap_left;
+                  y0 = m_line_yoffset + m_yoffset
+                       + (face->glyph->bitmap_top - 1);
+
+                  // 'w' seems to have a negative -1
+                  // face->glyph->bitmap_left, this is so we don't index out
+                  // of bound, and assumes we've allocated the right amount of
+                  // horizontal space in the bbox.
+                  if (x0 < 0)
+                    x0 = 0;
+
+                  for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++)
+                    for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++)
                       {
-                        prev_sub_name = sub_face->family_name;
-                        warning_with_id ("Octave:substituted-glyph",
-                                         "text_renderer: substituting font to '%s' for some characters",
-                                         sub_face->family_name);
+                        unsigned char pix
+                          = (m_antialias
+                             ? bitmap.buffer[r*bitmap.width+c]
+                             : (is_opaque (face->glyph, c, r) ? 255 : 0));
+
+                        if (x0+c < 0 || x0+c >= m_pixels.dim2 ()
+                            || y0-r < 0 || y0-r >= m_pixels.dim3 ())
+                          {
+                            // ::warning ("ft_text_renderer: x %d,  y %d",
+                            //            x0+c, y0-r);
+                          }
+                        else if (m_pixels(3, x0+c, y0-r).value () == 0)
+                          {
+                            m_pixels(0, x0+c, y0-r) = m_color(0);
+                            m_pixels(1, x0+c, y0-r) = m_color(1);
+                            m_pixels(2, x0+c, y0-r) = m_color(2);
+                            m_pixels(3, x0+c, y0-r) = pix;
+                          }
                       }
 
-                    ft_font saved_font = m_font;
+                  m_xoffset += (face->glyph->advance.x >> 6);
+                }
+              break;
 
-                    m_font = ft_font (m_font.get_name (), m_font.get_weight (),
-                                      m_font.get_angle (), m_font.get_size (),
-                                      sub_face);
-
-                    process_character (code, previous, sub_name);
+            case MODE_BBOX:
+              Matrix& bb = m_line_bbox.back ();
 
-                    m_font = saved_font;
-                  }
-                else
-                  {
-                    glyph_index = 0;
-                    warn_missing_glyph (code);
-                  }
-              }
-            else
-              {
-                glyph_index = 0;
-                warn_missing_glyph (code);
-              }
-#else
-            glyph_index = 0;
-            warn_missing_glyph (code);
-#endif
-          }
-        else if ((code == '\n') || (code == '\t'))
-          {
-            glyph_index = FT_Get_Char_Index (face, ' ');
-            if (! glyph_index
-                || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
-              {
-                glyph_index = 0;
-                warn_missing_glyph (' ');
-              }
-            else if (code == '\n')
-              push_new_line ();
-            else
-              {
-                // Advance to next multiple of 4 times the width of the "space"
-                // character.
-                int x_tab = 4 * (face->glyph->advance.x >> 6);
-                m_xoffset = (1 + std::floor (1. * m_xoffset / x_tab)) * x_tab;
-              }
-          }
-        else
-          {
-            switch (m_mode)
-              {
-              case MODE_RENDER:
-                if (FT_Render_Glyph (face->glyph, (m_antialias
-                                                   ? FT_RENDER_MODE_NORMAL
-                                                   : FT_RENDER_MODE_MONO)))
-                  {
-                    glyph_index = 0;
-                    warn_glyph_render (code);
-                  }
-                else
-                  {
-                    FT_Bitmap& bitmap = face->glyph->bitmap;
-                    int x0, y0;
+              // If we have a previous glyph, use kerning information.  This
+              // usually means moving a bit backward before adding the next
+              // glyph.  That is, "delta.x" is usually < 0.
+              if (previous)
+                {
+                  FT_Vector delta;
 
-                    if (previous)
-                      {
-                        FT_Vector delta;
+                  FT_Get_Kerning (face, previous, glyph_index,
+                                  FT_KERNING_DEFAULT, &delta);
 
-                        FT_Get_Kerning (face, previous, glyph_index,
-                                        FT_KERNING_DEFAULT, &delta);
-
-                        m_xoffset += (delta.x >> 6);
-                      }
+                  m_xoffset += (delta.x >> 6);
+                }
 
-                    x0 = m_xoffset + face->glyph->bitmap_left;
-                    y0 = m_line_yoffset + m_yoffset
-                         + (face->glyph->bitmap_top - 1);
+              // Extend current X offset box by the width of the current
+              // glyph.  Then extend the line bounding box if necessary.
 
-                    // 'w' seems to have a negative -1
-                    // face->glyph->bitmap_left, this is so we don't index out
-                    // of bound, and assumes we've allocated the right amount of
-                    // horizontal space in the bbox.
-                    if (x0 < 0)
-                      x0 = 0;
+              m_xoffset += (face->glyph->advance.x >> 6);
+              bb(2) = math::max (bb(2), m_xoffset);
 
-                    for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++)
-                      for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++)
-                        {
-                          unsigned char pix
-                            = (m_antialias
-                               ? bitmap.buffer[r*bitmap.width+c]
-                               : (is_opaque (face->glyph, c, r) ? 255 : 0));
-
-                          if (x0+c < 0 || x0+c >= m_pixels.dim2 ()
-                              || y0-r < 0 || y0-r >= m_pixels.dim3 ())
-                            {
-                              // ::warning ("ft_text_renderer: x %d,  y %d",
-                              //            x0+c, y0-r);
-                            }
-                          else if (m_pixels(3, x0+c, y0-r).value () == 0)
-                            {
-                              m_pixels(0, x0+c, y0-r) = m_color(0);
-                              m_pixels(1, x0+c, y0-r) = m_color(1);
-                              m_pixels(2, x0+c, y0-r) = m_color(2);
-                              m_pixels(3, x0+c, y0-r) = pix;
-                            }
-                        }
-
-                    m_xoffset += (face->glyph->advance.x >> 6);
-                  }
-                break;
-
-              case MODE_BBOX:
-                Matrix& bb = m_line_bbox.back ();
-
-                // If we have a previous glyph, use kerning information.  This
-                // usually means moving a bit backward before adding the next
-                // glyph.  That is, "delta.x" is usually < 0.
-                if (previous)
-                  {
-                    FT_Vector delta;
-
-                    FT_Get_Kerning (face, previous, glyph_index,
-                                    FT_KERNING_DEFAULT, &delta);
-
-                    m_xoffset += (delta.x >> 6);
-                  }
+              // Store the actual bbox vertical coordinates of this character
+              FT_Glyph glyph;
+              if (FT_Get_Glyph (face->glyph, &glyph))
+                warn_glyph_render (code);
+              else
+                {
+                  FT_BBox  glyph_bbox;
+                  FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_UNSCALED,
+                                     &glyph_bbox);
+                  m_deltax = (glyph_bbox.xMax - face->glyph->advance.x) >> 6;
+                  m_ymin = math::min ((glyph_bbox.yMin >> 6) + m_yoffset,
+                                      m_ymin);
+                  m_ymax = math::max ((glyph_bbox.yMax >> 6) + m_yoffset,
+                                      m_ymax);
+                  FT_Done_Glyph (glyph);
+                  update_line_bbox ();
+                }
+              break;
+            }
+        }
+    }
 
-                // Extend current X offset box by the width of the current
-                // glyph.  Then extend the line bounding box if necessary.
-
-                m_xoffset += (face->glyph->advance.x >> 6);
-                bb(2) = math::max (bb(2), m_xoffset);
+  return glyph_index;
+}
 
-                // Store the actual bbox vertical coordinates of this character
-                FT_Glyph glyph;
-                if (FT_Get_Glyph (face->glyph, &glyph))
-                  warn_glyph_render (code);
-                else
-                  {
-                    FT_BBox  glyph_bbox;
-                    FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_UNSCALED,
-                                       &glyph_bbox);
-                    m_deltax = (glyph_bbox.xMax - face->glyph->advance.x) >> 6;
-                    m_ymin = math::min ((glyph_bbox.yMin >> 6) + m_yoffset,
-                                        m_ymin);
-                    m_ymax = math::max ((glyph_bbox.yMax >> 6) + m_yoffset,
-                                        m_ymax);
-                    FT_Done_Glyph (glyph);
-                    update_line_bbox ();
-                  }
-                break;
-              }
-          }
-      }
+void
+ft_text_renderer::text_to_strlist (const std::string& txt,
+                                   std::list<text_renderer::string>& lst,
+                                   Matrix& box,
+                                   int ha, int va, double rot,
+                                   const caseless_str& interp)
+{
+  uint8NDArray pxls;
 
-    return glyph_index;
-  }
+  // First run text_to_pixels which will also build the string list
 
-  void
-  ft_text_renderer::text_to_strlist (const std::string& txt,
-                                     std::list<text_renderer::string>& lst,
-                                     Matrix& box,
-                                     int ha, int va, double rot,
-                                     const caseless_str& interp)
-  {
-    uint8NDArray pxls;
+  m_strlist = std::list<text_renderer::string> ();
 
-    // First run text_to_pixels which will also build the string list
-
-    m_strlist = std::list<text_renderer::string> ();
-
-    unwind_protect_var<bool> restore_var1 (m_do_strlist);
-    unwind_protect_var<std::list<text_renderer::string>>
+  unwind_protect_var<bool> restore_var1 (m_do_strlist);
+  unwind_protect_var<std::list<text_renderer::string>>
       restore_var2 (m_strlist);
 
-    m_do_strlist = true;
+  m_do_strlist = true;
 
-    text_to_pixels (txt, pxls, box, ha, va, rot, interp, false);
+  text_to_pixels (txt, pxls, box, ha, va, rot, interp, false);
 
-    lst = m_strlist;
-  }
+  lst = m_strlist;
+}
 
-  void
-  ft_text_renderer::visit (text_element_string& e)
-  {
-    if (m_font.is_valid ())
-      {
-        m_max_fontsize = std::max (m_max_fontsize, m_font.get_size ());
-        FT_UInt glyph_index, previous = 0;
+void
+ft_text_renderer::visit (text_element_string& e)
+{
+  if (m_font.is_valid ())
+    {
+      m_max_fontsize = std::max (m_max_fontsize, m_font.get_size ());
+      FT_UInt glyph_index, previous = 0;
 
-        std::string str = e.string_value ();
-        const uint8_t *c = reinterpret_cast<const uint8_t *> (str.c_str ());
-        uint32_t u32_c;
+      std::string str = e.string_value ();
+      const uint8_t *c = reinterpret_cast<const uint8_t *> (str.c_str ());
+      uint32_t u32_c;
 
-        std::size_t n = str.size ();
-        std::size_t icurr = 0;
-        std::size_t ibegin = 0;
+      std::size_t n = str.size ();
+      std::size_t icurr = 0;
+      std::size_t ibegin = 0;
 
-        // Initialize a new string
-        text_renderer::string fs (str, m_font, m_xoffset, m_yoffset);
+      // Initialize a new string
+      text_renderer::string fs (str, m_font, m_xoffset, m_yoffset);
 
-        std::string fname = m_font.get_face ()->family_name;
+      std::string fname = m_font.get_face ()->family_name;
 
-        if (fname.find (" ") != std::string::npos)
-          fname = "'" + fname + "'";
+      if (fname.find (" ") != std::string::npos)
+        fname = "'" + fname + "'";
 
-        fs.set_family (fname);
+      fs.set_family (fname);
 
-        std::vector<double> xdata;
-        std::string sub_name;
+      std::vector<double> xdata;
+      std::string sub_name;
 
-        while (n > 0)
-          {
-            // Retrieve the length and the u32 representation of the current
-            // character
-            int mblen = octave_u8_strmbtouc_wrapper (&u32_c, c + icurr);
-            if (mblen < 1)
-              {
-                // This is not an UTF-8 character, use a replacement character
-                mblen = 1;
-                u32_c = 0xFFFD;
-              }
+      while (n > 0)
+        {
+          // Retrieve the length and the u32 representation of the current
+          // character
+          int mblen = octave_u8_strmbtouc_wrapper (&u32_c, c + icurr);
+          if (mblen < 1)
+            {
+              // This is not an UTF-8 character, use a replacement character
+              mblen = 1;
+              u32_c = 0xFFFD;
+            }
 
-            n -= mblen;
+          n -= mblen;
 
-            if (m_do_strlist && m_mode == MODE_RENDER)
-              {
-                if (u32_c == 10)
-                  {
-                    // Finish previous string in m_strlist before processing
-                    // the newline character
-                    fs.set_y (m_line_yoffset + m_yoffset);
-                    fs.set_color (m_color);
+          if (m_do_strlist && m_mode == MODE_RENDER)
+            {
+              if (u32_c == 10)
+                {
+                  // Finish previous string in m_strlist before processing
+                  // the newline character
+                  fs.set_y (m_line_yoffset + m_yoffset);
+                  fs.set_color (m_color);
 
-                    std::string s = str.substr (ibegin, icurr - ibegin);
-                    if (! s.empty ())
-                      {
-                        fs.set_string (s);
-                        fs.set_y (m_line_yoffset + m_yoffset);
-                        fs.set_xdata (xdata);
-                        fs.set_family (fname);
-                        m_strlist.push_back (fs);
-                      }
-                  }
-                else
-                  xdata.push_back (m_xoffset);
-              }
+                  std::string s = str.substr (ibegin, icurr - ibegin);
+                  if (! s.empty ())
+                    {
+                      fs.set_string (s);
+                      fs.set_y (m_line_yoffset + m_yoffset);
+                      fs.set_xdata (xdata);
+                      fs.set_family (fname);
+                      m_strlist.push_back (fs);
+                    }
+                }
+              else
+                xdata.push_back (m_xoffset);
+            }
 
-            glyph_index = process_character (u32_c, previous, sub_name);
+          glyph_index = process_character (u32_c, previous, sub_name);
 
-            if (m_do_strlist && m_mode == MODE_RENDER && ! sub_name.empty ())
-              {
-                // Add substitution font to the family name stack
-                std::string tmp_family = fs.get_family ();
+          if (m_do_strlist && m_mode == MODE_RENDER && ! sub_name.empty ())
+            {
+              // Add substitution font to the family name stack
+              std::string tmp_family = fs.get_family ();
 
-                if (tmp_family.find (sub_name) == std::string::npos)
-                  {
-                    if (sub_name.find (" ") != std::string::npos)
-                      sub_name = "'" + sub_name + "'";
+              if (tmp_family.find (sub_name) == std::string::npos)
+                {
+                  if (sub_name.find (" ") != std::string::npos)
+                    sub_name = "'" + sub_name + "'";
 
-                    fs.set_family (tmp_family + ", " + sub_name);
-                  }
-              }
+                  fs.set_family (tmp_family + ", " + sub_name);
+                }
+            }
 
-            if (u32_c == 10)
-              {
-                previous = 0;
+          if (u32_c == 10)
+            {
+              previous = 0;
 
-                if (m_do_strlist && m_mode == MODE_RENDER)
-                  {
-                    // Start a new string in m_strlist
-                    ibegin = icurr+1;
-                    xdata.clear ();
-                    fs = text_renderer::string (str.substr (ibegin), m_font,
-                                                m_line_xoffset, m_yoffset);
-                  }
-              }
-            else
-              previous = glyph_index;
+              if (m_do_strlist && m_mode == MODE_RENDER)
+                {
+                  // Start a new string in m_strlist
+                  ibegin = icurr+1;
+                  xdata.clear ();
+                  fs = text_renderer::string (str.substr (ibegin), m_font,
+                                              m_line_xoffset, m_yoffset);
+                }
+            }
+          else
+            previous = glyph_index;
 
-            icurr += mblen;
-          }
+          icurr += mblen;
+        }
 
-        if (m_do_strlist && m_mode == MODE_RENDER
-            && ! fs.get_string ().empty ())
-          {
-            fs.set_y (m_line_yoffset + m_yoffset);
-            fs.set_color (m_color);
-            fs.set_xdata (xdata);
-            m_strlist.push_back (fs);
-          }
-      }
-  }
+      if (m_do_strlist && m_mode == MODE_RENDER
+          && ! fs.get_string ().empty ())
+        {
+          fs.set_y (m_line_yoffset + m_yoffset);
+          fs.set_color (m_color);
+          fs.set_xdata (xdata);
+          m_strlist.push_back (fs);
+        }
+    }
+}
 
-  void
-  ft_text_renderer::visit (text_element_list& e)
-  {
-    // Save and restore (after processing the list) the current font and color.
+void
+ft_text_renderer::visit (text_element_list& e)
+{
+  // Save and restore (after processing the list) the current font and color.
 
-    ft_font saved_font (m_font);
-    uint8NDArray saved_color (m_color);
+  ft_font saved_font (m_font);
+  uint8NDArray saved_color (m_color);
 
-    text_processor::visit (e);
+  text_processor::visit (e);
 
-    m_font = saved_font;
-    m_color = saved_color;
-  }
+  m_font = saved_font;
+  m_color = saved_color;
+}
 
-  void
-  ft_text_renderer::visit (text_element_subscript& e)
-  {
-    ft_font saved_font (m_font);
-    int saved_line_yoffset = m_line_yoffset;
-    int saved_yoffset = m_yoffset;
+void
+ft_text_renderer::visit (text_element_subscript& e)
+{
+  ft_font saved_font (m_font);
+  int saved_line_yoffset = m_line_yoffset;
+  int saved_yoffset = m_yoffset;
 
-    double sz = m_font.get_size ();
+  double sz = m_font.get_size ();
 
-    // Reducing font size by 70% produces decent results.
-    set_font (m_font.get_name (), m_font.get_weight (), m_font.get_angle (),
-              std::max (5.0, sz * 0.7));
+  // Reducing font size by 70% produces decent results.
+  set_font (m_font.get_name (), m_font.get_weight (), m_font.get_angle (),
+            std::max (5.0, sz * 0.7));
 
-    if (m_font.is_valid ())
-      {
-        // Shifting the baseline by 15% of the font size gives decent results.
-        m_yoffset -= std::ceil (sz * 0.15);
+  if (m_font.is_valid ())
+    {
+      // Shifting the baseline by 15% of the font size gives decent results.
+      m_yoffset -= std::ceil (sz * 0.15);
 
-        if (m_mode == MODE_BBOX)
-          update_line_bbox ();
-      }
+      if (m_mode == MODE_BBOX)
+        update_line_bbox ();
+    }
 
-    text_processor::visit (e);
+  text_processor::visit (e);
 
-    m_font = saved_font;
-    // If line_yoffset changed, this means we moved to a new line; hence yoffset
-    // cannot be restored, because the saved value is not relevant anymore.
-    if (m_line_yoffset == saved_line_yoffset)
-      m_yoffset = saved_yoffset;
-  }
+  m_font = saved_font;
+  // If line_yoffset changed, this means we moved to a new line; hence yoffset
+  // cannot be restored, because the saved value is not relevant anymore.
+  if (m_line_yoffset == saved_line_yoffset)
+    m_yoffset = saved_yoffset;
+}
 
-  void
-  ft_text_renderer::visit (text_element_superscript& e)
-  {
-    ft_font saved_font (m_font);
-    int saved_line_yoffset = m_line_yoffset;
-    int saved_yoffset = m_yoffset;
+void
+ft_text_renderer::visit (text_element_superscript& e)
+{
+  ft_font saved_font (m_font);
+  int saved_line_yoffset = m_line_yoffset;
+  int saved_yoffset = m_yoffset;
 
-    double sz = m_font.get_size ();
+  double sz = m_font.get_size ();
 
-    // Reducing font size by 70% produces decent results.
-    set_font (m_font.get_name (), m_font.get_weight (), m_font.get_angle (),
-              std::max (5.0, sz * 0.7));
+  // Reducing font size by 70% produces decent results.
+  set_font (m_font.get_name (), m_font.get_weight (), m_font.get_angle (),
+            std::max (5.0, sz * 0.7));
 
-    if (saved_font.is_valid ())
-      {
-        // Shifting the baseline by 40% of the font size gives decent results.
-        m_yoffset += std::ceil (sz * 0.4);
+  if (saved_font.is_valid ())
+    {
+      // Shifting the baseline by 40% of the font size gives decent results.
+      m_yoffset += std::ceil (sz * 0.4);
 
-        if (m_mode == MODE_BBOX)
-          update_line_bbox ();
-      }
+      if (m_mode == MODE_BBOX)
+        update_line_bbox ();
+    }
 
-    text_processor::visit (e);
+  text_processor::visit (e);
 
-    m_font = saved_font;
-    // If line_yoffset changed, this means we moved to a new line; hence yoffset
-    // cannot be restored, because the saved value is not relevant anymore.
-    if (m_line_yoffset == saved_line_yoffset)
-      m_yoffset = saved_yoffset;
-  }
+  m_font = saved_font;
+  // If line_yoffset changed, this means we moved to a new line; hence yoffset
+  // cannot be restored, because the saved value is not relevant anymore.
+  if (m_line_yoffset == saved_line_yoffset)
+    m_yoffset = saved_yoffset;
+}
 
-  void
-  ft_text_renderer::visit (text_element_color& e)
-  {
-    if (m_mode == MODE_RENDER)
-      set_color (e.get_color ());
-  }
+void
+ft_text_renderer::visit (text_element_color& e)
+{
+  if (m_mode == MODE_RENDER)
+    set_color (e.get_color ());
+}
 
-  void
-  ft_text_renderer::visit (text_element_fontsize& e)
-  {
-    double sz = e.get_fontsize ();
+void
+ft_text_renderer::visit (text_element_fontsize& e)
+{
+  double sz = e.get_fontsize ();
 
-    // FIXME: Matlab documentation says that the font size is expressed
-    //        in the text object FontUnit.
+  // FIXME: Matlab documentation says that the font size is expressed
+  //        in the text object FontUnit.
 
-    set_font (m_font.get_name (), m_font.get_weight (),
-              m_font.get_angle (), sz);
+  set_font (m_font.get_name (), m_font.get_weight (),
+            m_font.get_angle (), sz);
 
-    if (m_mode == MODE_BBOX)
-      update_line_bbox ();
-  }
+  if (m_mode == MODE_BBOX)
+    update_line_bbox ();
+}
 
-  void
-  ft_text_renderer::visit (text_element_fontname& e)
-  {
-    set_font (e.get_fontname (), m_font.get_weight (), m_font.get_angle (),
-              m_font.get_size ());
+void
+ft_text_renderer::visit (text_element_fontname& e)
+{
+  set_font (e.get_fontname (), m_font.get_weight (), m_font.get_angle (),
+            m_font.get_size ());
 
-    if (m_mode == MODE_BBOX)
-      update_line_bbox ();
-  }
+  if (m_mode == MODE_BBOX)
+    update_line_bbox ();
+}
 
-  void
-  ft_text_renderer::visit (text_element_fontstyle& e)
-  {
-    switch (e.get_fontstyle ())
-      {
-      case text_element_fontstyle::normal:
-        set_font (m_font.get_name (), "normal", "normal", m_font.get_size ());
-        break;
+void
+ft_text_renderer::visit (text_element_fontstyle& e)
+{
+  switch (e.get_fontstyle ())
+    {
+    case text_element_fontstyle::normal:
+      set_font (m_font.get_name (), "normal", "normal", m_font.get_size ());
+      break;
 
-      case text_element_fontstyle::bold:
-        set_font (m_font.get_name (), "bold", "normal", m_font.get_size ());
-        break;
+    case text_element_fontstyle::bold:
+      set_font (m_font.get_name (), "bold", "normal", m_font.get_size ());
+      break;
 
-      case text_element_fontstyle::italic:
-        set_font (m_font.get_name (), "normal", "italic", m_font.get_size ());
-        break;
+    case text_element_fontstyle::italic:
+      set_font (m_font.get_name (), "normal", "italic", m_font.get_size ());
+      break;
 
-      case text_element_fontstyle::oblique:
-        set_font (m_font.get_name (), "normal", "oblique", m_font.get_size ());
-        break;
-      }
+    case text_element_fontstyle::oblique:
+      set_font (m_font.get_name (), "normal", "oblique", m_font.get_size ());
+      break;
+    }
 
-    if (m_mode == MODE_BBOX)
-      update_line_bbox ();
-  }
+  if (m_mode == MODE_BBOX)
+    update_line_bbox ();
+}
 
-  void
-  ft_text_renderer::visit (text_element_symbol& e)
-  {
-    uint32_t code = e.get_symbol_code ();
+void
+ft_text_renderer::visit (text_element_symbol& e)
+{
+  uint32_t code = e.get_symbol_code ();
 
-    std::vector<double> xdata (1, m_xoffset);
-    text_renderer::string fs ("-", m_font, m_xoffset, m_yoffset);
+  std::vector<double> xdata (1, m_xoffset);
+  text_renderer::string fs ("-", m_font, m_xoffset, m_yoffset);
 
-    if (code != text_element_symbol::invalid_code && m_font.is_valid ())
-      {
-        std::string sub_name;
+  if (code != text_element_symbol::invalid_code && m_font.is_valid ())
+    {
+      std::string sub_name;
 
-        process_character (code, 0, sub_name);
+      process_character (code, 0, sub_name);
 
-        if (m_do_strlist && m_mode == MODE_RENDER)
-          {
-            if (! sub_name.empty ())
-              {
-                // Add substitution font to the family name
-                std::string tmp_family = fs.get_family ();
+      if (m_do_strlist && m_mode == MODE_RENDER)
+        {
+          if (! sub_name.empty ())
+            {
+              // Add substitution font to the family name
+              std::string tmp_family = fs.get_family ();
 
-                if (tmp_family.find (sub_name) == std::string::npos)
-                  {
-                    if (sub_name.find (" ") != std::string::npos)
-                      sub_name = "'" + sub_name + "'";
+              if (tmp_family.find (sub_name) == std::string::npos)
+                {
+                  if (sub_name.find (" ") != std::string::npos)
+                    sub_name = "'" + sub_name + "'";
 
-                    fs.set_family (tmp_family + ", " + sub_name);
-                  }
-              }
+                  fs.set_family (tmp_family + ", " + sub_name);
+                }
+            }
 
-            fs.set_code (code);
-            fs.set_xdata (xdata);
-          }
-      }
-    else if (m_font.is_valid ())
-      ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
+          fs.set_code (code);
+          fs.set_xdata (xdata);
+        }
+    }
+  else if (m_font.is_valid ())
+    ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
 
-    if (m_do_strlist && m_mode == MODE_RENDER && fs.get_code ())
-      {
-        fs.set_y (m_line_yoffset + m_yoffset);
-        fs.set_color (m_color);
-        fs.set_family (m_font.get_face ()->family_name);
-        m_strlist.push_back (fs);
-      }
-  }
+  if (m_do_strlist && m_mode == MODE_RENDER && fs.get_code ())
+    {
+      fs.set_y (m_line_yoffset + m_yoffset);
+      fs.set_color (m_color);
+      fs.set_family (m_font.get_face ()->family_name);
+      m_strlist.push_back (fs);
+    }
+}
 
-  void
-  ft_text_renderer::visit (text_element_combined& e)
-  {
-    int saved_xoffset = m_xoffset;
-    int max_xoffset = m_xoffset;
+void
+ft_text_renderer::visit (text_element_combined& e)
+{
+  int saved_xoffset = m_xoffset;
+  int max_xoffset = m_xoffset;
 
-    for (auto *txt_elt : e)
-      {
-        m_xoffset = saved_xoffset;
-        txt_elt->accept (*this);
-        max_xoffset = math::max (m_xoffset, max_xoffset);
-      }
+  for (auto *txt_elt : e)
+    {
+      m_xoffset = saved_xoffset;
+      txt_elt->accept (*this);
+      max_xoffset = math::max (m_xoffset, max_xoffset);
+    }
 
-    m_xoffset = max_xoffset;
-  }
+  m_xoffset = max_xoffset;
+}
 
-  void
-  ft_text_renderer::reset (void)
-  {
-    set_mode (MODE_BBOX);
-    set_color (Matrix (1, 3, 0.0));
-    m_strlist = std::list<text_renderer::string> ();
-  }
+void
+ft_text_renderer::reset (void)
+{
+  set_mode (MODE_BBOX);
+  set_color (Matrix (1, 3, 0.0));
+  m_strlist = std::list<text_renderer::string> ();
+}
 
-  void
-  ft_text_renderer::set_color (const Matrix& c)
-  {
-    if (c.numel () == 3)
-      {
-        m_color(0) = static_cast<uint8_t> (c(0)*255);
-        m_color(1) = static_cast<uint8_t> (c(1)*255);
-        m_color(2) = static_cast<uint8_t> (c(2)*255);
-      }
-    else
-      ::warning ("ft_text_renderer::set_color: invalid color");
-  }
+void
+ft_text_renderer::set_color (const Matrix& c)
+{
+  if (c.numel () == 3)
+    {
+      m_color(0) = static_cast<uint8_t> (c(0)*255);
+      m_color(1) = static_cast<uint8_t> (c(1)*255);
+      m_color(2) = static_cast<uint8_t> (c(2)*255);
+    }
+  else
+    ::warning ("ft_text_renderer::set_color: invalid color");
+}
 
-  uint8NDArray
-  ft_text_renderer::render (text_element *elt, Matrix& box, int rotation)
-  {
-    set_mode (MODE_BBOX);
-    elt->accept (*this);
-    compute_bbox ();
-    box = m_bbox;
+uint8NDArray
+ft_text_renderer::render (text_element *elt, Matrix& box, int rotation)
+{
+  set_mode (MODE_BBOX);
+  elt->accept (*this);
+  compute_bbox ();
+  box = m_bbox;
 
-    set_mode (MODE_RENDER);
+  set_mode (MODE_RENDER);
 
-    if (m_pixels.numel () > 0)
-      {
-        elt->accept (*this);
+  if (m_pixels.numel () > 0)
+    {
+      elt->accept (*this);
 
-        rotate_pixels (m_pixels, rotation);
-      }
+      rotate_pixels (m_pixels, rotation);
+    }
 
-    return m_pixels;
-  }
+  return m_pixels;
+}
 
-  // Note:
-  // x-extent accurately measures width of glyphs.
-  // y-extent is overly large because it is measured from baseline-to-baseline.
-  // Calling routines, such as ylabel, may need to account for this mismatch.
+// Note:
+// x-extent accurately measures width of glyphs.
+// y-extent is overly large because it is measured from baseline-to-baseline.
+// Calling routines, such as ylabel, may need to account for this mismatch.
 
-  Matrix
-  ft_text_renderer::get_extent (text_element *elt, double rotation)
-  {
-    set_mode (MODE_BBOX);
-    elt->accept (*this);
-    compute_bbox ();
+Matrix
+ft_text_renderer::get_extent (text_element *elt, double rotation)
+{
+  set_mode (MODE_BBOX);
+  elt->accept (*this);
+  compute_bbox ();
 
-    Matrix extent (1, 2, 0.0);
+  Matrix extent (1, 2, 0.0);
 
-    switch (rotation_to_mode (rotation))
-      {
-      case ROTATION_0:
-      case ROTATION_180:
-        extent(0) = m_bbox(2);
-        extent(1) = m_bbox(3);
-        break;
+  switch (rotation_to_mode (rotation))
+    {
+    case ROTATION_0:
+    case ROTATION_180:
+      extent(0) = m_bbox(2);
+      extent(1) = m_bbox(3);
+      break;
 
-      case ROTATION_90:
-      case ROTATION_270:
-        extent(0) = m_bbox(3);
-        extent(1) = m_bbox(2);
-      }
+    case ROTATION_90:
+    case ROTATION_270:
+      extent(0) = m_bbox(3);
+      extent(1) = m_bbox(2);
+    }
 
-    return extent;
-  }
+  return extent;
+}
 
-  Matrix
-  ft_text_renderer::get_extent (const std::string& txt, double rotation,
-                                const caseless_str& interpreter)
-  {
-    text_element *elt = text_parser::parse (txt, interpreter);
-    Matrix extent = get_extent (elt, rotation);
-    delete elt;
+Matrix
+ft_text_renderer::get_extent (const std::string& txt, double rotation,
+                              const caseless_str& interpreter)
+{
+  text_element *elt = text_parser::parse (txt, interpreter);
+  Matrix extent = get_extent (elt, rotation);
+  delete elt;
 
-    return extent;
-  }
+  return extent;
+}
 
-  void
-  ft_text_renderer::text_to_pixels (const std::string& txt,
-                                    uint8NDArray& pxls, Matrix& box,
-                                    int _halign, int valign, double rotation,
-                                    const caseless_str& interpreter,
-                                    bool handle_rotation)
-  {
-    int rot_mode = rotation_to_mode (rotation);
+void
+ft_text_renderer::text_to_pixels (const std::string& txt,
+                                  uint8NDArray& pxls, Matrix& box,
+                                  int _halign, int valign, double rotation,
+                                  const caseless_str& interpreter,
+                                  bool handle_rotation)
+{
+  int rot_mode = rotation_to_mode (rotation);
 
-    m_halign = _halign;
+  m_halign = _halign;
 
-    text_element *elt = text_parser::parse (txt, interpreter);
-    pxls = render (elt, box, rot_mode);
-    delete elt;
+  text_element *elt = text_parser::parse (txt, interpreter);
+  pxls = render (elt, box, rot_mode);
+  delete elt;
 
-    if (pxls.isempty ())
-      return;  // nothing to render
+  if (pxls.isempty ())
+    return;  // nothing to render
 
-    // Move X0 and Y0 depending on alignments and eventually swap all values
-    // for text rotated 90° 180° or 270°
-    fix_bbox_anchor (box, m_halign, valign, rot_mode, handle_rotation);
-  }
+  // Move X0 and Y0 depending on alignments and eventually swap all values
+  // for text rotated 90° 180° or 270°
+  fix_bbox_anchor (box, m_halign, valign, rot_mode, handle_rotation);
+}
 
-  ft_text_renderer::ft_font::ft_font (const ft_font& ft)
-    : text_renderer::font (ft), m_face (nullptr)
-  {
+ft_text_renderer::ft_font::ft_font (const ft_font& ft)
+  : text_renderer::font (ft), m_face (nullptr)
+{
 #if defined (HAVE_FT_REFERENCE_FACE)
-    FT_Face ft_face = ft.get_face ();
+  FT_Face ft_face = ft.get_face ();
 
-    if (ft_face && FT_Reference_Face (ft_face) == 0)
-      m_face = ft_face;
+  if (ft_face && FT_Reference_Face (ft_face) == 0)
+    m_face = ft_face;
 #endif
-  }
+}
 
-  ft_text_renderer::ft_font&
-  ft_text_renderer::ft_font::operator = (const ft_font& ft)
-  {
-    if (&ft != this)
-      {
-        text_renderer::font::operator = (ft);
+ft_text_renderer::ft_font&
+ft_text_renderer::ft_font::operator = (const ft_font& ft)
+{
+  if (&ft != this)
+    {
+      text_renderer::font::operator = (ft);
 
-        if (m_face)
-          {
-            FT_Done_Face (m_face);
-            m_face = nullptr;
-          }
+      if (m_face)
+        {
+          FT_Done_Face (m_face);
+          m_face = nullptr;
+        }
 
 #if defined (HAVE_FT_REFERENCE_FACE)
-        FT_Face ft_face = ft.get_face ();
+      FT_Face ft_face = ft.get_face ();
 
-        if (ft_face && FT_Reference_Face (ft_face) == 0)
-          m_face = ft_face;
+      if (ft_face && FT_Reference_Face (ft_face) == 0)
+        m_face = ft_face;
 #endif
-      }
+    }
 
-    return *this;
-  }
+  return *this;
+}
 
-  FT_Face
-  ft_text_renderer::ft_font::get_face (void) const
-  {
-    if (! m_face && ! m_name.empty ())
-      {
-        m_face = ft_manager::get_font (m_name, m_weight, m_angle, m_size);
+FT_Face
+ft_text_renderer::ft_font::get_face (void) const
+{
+  if (! m_face && ! m_name.empty ())
+    {
+      m_face = ft_manager::get_font (m_name, m_weight, m_angle, m_size);
 
-        if (m_face)
-          {
-            if (FT_Set_Char_Size (m_face, 0, m_size*64, 0, 0))
-              ::warning ("ft_text_renderer: unable to set font size to %g", m_size);
-          }
-        else
-          ::warning ("ft_text_renderer: unable to load appropriate font");
-      }
+      if (m_face)
+        {
+          if (FT_Set_Char_Size (m_face, 0, m_size*64, 0, 0))
+            ::warning ("ft_text_renderer: unable to set font size to %g", m_size);
+        }
+      else
+        ::warning ("ft_text_renderer: unable to load appropriate font");
+    }
 
-    return m_face;
-  }
+  return m_face;
+}
 
 OCTAVE_END_NAMESPACE(octave)
 
@@ -1533,14 +1533,14 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-  base_text_renderer *
-  make_ft_text_renderer (void)
-  {
+base_text_renderer *
+make_ft_text_renderer (void)
+{
 #if defined (HAVE_FREETYPE)
-    return new ft_text_renderer ();
+  return new ft_text_renderer ();
 #else
-    return 0;
+  return 0;
 #endif
-  }
+}
 
 OCTAVE_END_NAMESPACE(octave)