changeset 21209:67d2965af0b5

revamp text rendering classes * base-text-renderer.h: New file. * ft-text-renderer.h, ft-text-renderer.cc: New files for freetype text rendering classes, adapted from txt-eng-ft.h and txt-eng.cc. * text-renderer.h, text-renderer.cc: New files. Public interface for text rendering. * gl-select.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, graphics.cc, graphics.in.h: Adapt to use new text rendering interface that does not require checking HAVE_FREETYPE. * libinterp/corefcn/module.mk: Update.
author John W. Eaton <jwe@octave.org>
date Sat, 06 Feb 2016 08:15:53 -0500
parents f5e05c11c343
children 4f7d3989c462
files libgui/graphics/gl-select.cc libinterp/corefcn/base-text-renderer.h libinterp/corefcn/ft-text-renderer.cc libinterp/corefcn/ft-text-renderer.h libinterp/corefcn/gl-render.cc libinterp/corefcn/gl-render.h libinterp/corefcn/gl2ps-print.cc libinterp/corefcn/graphics.cc libinterp/corefcn/graphics.in.h libinterp/corefcn/module.mk libinterp/corefcn/text-renderer.cc libinterp/corefcn/text-renderer.h libinterp/corefcn/txt-eng-ft.cc libinterp/corefcn/txt-eng-ft.h
diffstat 14 files changed, 1925 insertions(+), 1533 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/graphics/gl-select.cc	Fri Feb 05 14:53:10 2016 -0500
+++ b/libgui/graphics/gl-select.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -187,19 +187,15 @@
                               double x, double y, double z,
                               int halign, int valign, double rotation)
 {
-#if HAVE_FREETYPE
   uint8NDArray pixels;
-  Matrix bbox;
+  Matrix bbox (1, 4, 0.0);
 
   // FIXME: probably more efficient to only compute bbox instead
   //        of doing full text rendering...
   text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
-  fake_text (x, y, z, bbox, false);
+  fake_text(x, y, z, bbox, false);
 
   return bbox;
-#else
-  return Matrix (1, 4, 0.0);
-#endif
 }
 
 void
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/base-text-renderer.h	Sat Feb 06 08:15:53 2016 -0500
@@ -0,0 +1,79 @@
+/*
+
+Copyright (C) 2016 John W. Eaton
+Copyright (C) 2009-2015 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_base_text_renderer_h)
+#define octave_base_text_renderer_h 1
+
+#include <list>
+#include <string>
+
+#include "dMatrix.h"
+#include "uint8NDArray.h"
+
+#include "text-renderer.h"
+#include "txt-eng.h"
+
+class
+base_text_renderer : public text_processor
+{
+public:
+
+  base_text_renderer (void) : text_processor () { }
+
+  virtual ~base_text_renderer (void) { }
+
+  virtual Matrix
+  get_extent (text_element *elt, double rotation) = 0;
+
+  virtual Matrix
+  get_extent (const std::string& txt, double rotation,
+              const caseless_str& interpreter) = 0;
+
+  virtual void
+  set_font (const std::string& name, const std::string& weight,
+            const std::string& angle, double size) = 0;
+
+  virtual void set_color (const Matrix& c) = 0;
+
+  virtual 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) = 0;
+
+  virtual void
+  text_to_strlist (const std::string& txt,
+                   std::list<text_renderer::string>& lst,
+                   Matrix& box, int halign, int valign, double rotation,
+                   const caseless_str& interpreter = "tex") = 0;
+
+private:
+
+  // No copying!
+
+  base_text_renderer (const base_text_renderer&);
+
+  base_text_renderer& operator = (const base_text_renderer&);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/ft-text-renderer.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -0,0 +1,1367 @@
+/*
+
+Copyright (C) 2016 John W. Eaton
+Copyright (C) 2009-2015 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "base-text-renderer.h"
+
+#if defined (HAVE_FREETYPE)
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if defined (HAVE_FONTCONFIG)
+#  include <fontconfig/fontconfig.h>
+#endif
+
+#include <clocale>
+#include <cwchar>
+#include <iostream>
+#include <map>
+#include <utility>
+
+#include "singleton-cleanup.h"
+
+#include "error.h"
+#include "pr-output.h"
+#include "text-renderer.h"
+
+// 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 '%x'", c);
+}
+
+static void
+warn_glyph_render (FT_ULong c)
+{
+  warning_with_id ("Octave:glyph-render",
+                   "text_renderer: unable to render glyph for character '%x'", 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.
+#  include "PermMatrix.h"
+#endif
+
+// Forward declaration
+static void ft_face_destroyed (void *object);
+
+class
+ft_manager
+{
+public:
+  static bool instance_ok (void)
+  {
+    bool retval = true;
+
+    if (! instance)
+      {
+        instance = new ft_manager ();
+
+        if (instance)
+          singleton_cleanup_list::add (cleanup_instance);
+      }
+
+    if (! instance)
+      error ("unable to create ft_manager!");
+
+    return retval;
+  }
+
+  static void cleanup_instance (void) { delete instance; instance = 0; }
+
+  static FT_Face get_font (const std::string& name, const std::string& weight,
+                           const std::string& angle, double size)
+  {
+    return (instance_ok ()
+            ? instance->do_get_font (name, weight, angle, size)
+            : 0);
+  }
+
+  static void font_destroyed (FT_Face face)
+  {
+    if (instance_ok ())
+      instance->do_font_destroyed (face);
+  }
+
+private:
+
+  static ft_manager *instance;
+
+  typedef std::pair<std::string, double> ft_key;
+  typedef std::map<ft_key, FT_Face> ft_cache;
+
+  // 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 cache;
+
+private:
+
+  // No copying!
+
+  ft_manager (const ft_manager&);
+
+  ft_manager& operator = (const ft_manager&);
+
+  ft_manager (void)
+    : library (), freetype_initialized (false), fontconfig_initialized (false)
+  {
+    if (FT_Init_FreeType (&library))
+      error ("unable to initialize FreeType library");
+    else
+      freetype_initialized = true;
+
+#if defined (HAVE_FONTCONFIG)
+    if (! FcInit ())
+      error ("unable to initialize fontconfig library");
+    else
+      fontconfig_initialized = true;
+#endif
+  }
+
+  ~ft_manager (void)
+  {
+    if (freetype_initialized)
+      FT_Done_FreeType (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 (fontconfig_initialized)
+    //   FcFini ();
+#endif
+  }
+
+
+  FT_Face do_get_font (const std::string& name, const std::string& weight,
+                       const std::string& angle, double size)
+  {
+    FT_Face retval = 0;
+
+#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, size);
+    ft_cache::const_iterator it = cache.find (key);
+
+    if (it != cache.end ())
+      {
+        FT_Reference_Face (it->second);
+        return it->second;
+      }
+#endif
+
+    std::string file;
+
+#if defined (HAVE_FONTCONFIG)
+    if (fontconfig_initialized)
+      {
+        int fc_weight, fc_angle;
+
+        if (weight == "bold")
+          fc_weight = FC_WEIGHT_BOLD;
+        else if (weight == "light")
+          fc_weight = FC_WEIGHT_LIGHT;
+        else if (weight == "demi")
+          fc_weight = FC_WEIGHT_DEMIBOLD;
+        else
+          fc_weight = FC_WEIGHT_NORMAL;
+
+        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 == "*" ? "sans" : name.c_str ())));
+
+        FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
+        FcPatternAddInteger (pat, FC_SLANT, fc_angle);
+        FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
+
+        if (FcConfigSubstitute (0, pat, FcMatchPattern))
+          {
+            FcResult res;
+            FcPattern *match;
+
+            FcDefaultSubstitute (pat);
+            match = FcFontMatch (0, 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",
+                         name.c_str (), weight.c_str (), angle.c_str (),
+                         size);
+
+            if (match)
+              FcPatternDestroy (match);
+          }
+
+        FcPatternDestroy (pat);
+      }
+#endif
+
+    if (file.empty ())
+      {
+#if defined (__WIN32__)
+        file = "C:/WINDOWS/Fonts/verdana.ttf";
+#else
+        // FIXME: find a "standard" font for UNIX platforms
+#endif
+      }
+
+    if (! file.empty ())
+      {
+        if (FT_New_Face (library, 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.
+
+            retval->generic.data = new ft_key (key);
+            retval->generic.finalizer = ft_face_destroyed;
+
+            // Insert loaded font into the cache.
+
+            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);
+
+        cache.erase (*pkey);
+        delete pkey;
+        face->generic.data = 0;
+      }
+  }
+
+private:
+  FT_Library library;
+  bool freetype_initialized;
+  bool fontconfig_initialized;
+};
+
+ft_manager *ft_manager::instance = 0;
+
+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
+  };
+
+  enum
+  {
+    ROTATION_0   = 0,
+    ROTATION_90  = 1,
+    ROTATION_180 = 2,
+    ROTATION_270 = 3
+  };
+
+public:
+
+  ft_text_renderer (void)
+    : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0),
+      xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX),
+      color (dim_vector (1, 3), 0)
+  { }
+
+  ~ft_text_renderer (void) { }
+
+  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 pixels; }
+
+  Matrix get_boundingbox (void) const { return 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_font (const std::string& name, const std::string& weight,
+                 const std::string& angle, double size);
+
+  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:
+
+  int rotation_to_mode (double rotation) const;
+
+  // No copying!
+
+  ft_text_renderer (const ft_text_renderer&);
+
+  ft_text_renderer& operator = (const ft_text_renderer&);
+
+  // 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 (), face (0) { }
+
+    ft_font (const std::string& nm, const std::string& wt,
+             const std::string& ang, double sz, FT_Face f = 0)
+      : text_renderer::font (nm, wt, ang, sz), face (f)
+    { }
+
+    ft_font (const ft_font& ft);
+
+    ~ft_font (void)
+    {
+      if (face)
+        FT_Done_Face (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 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 = 0);
+
+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 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 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 pixels;
+
+  // Used to store the bounding box of each line. This is used to layout
+  // multiline text properly.
+  std::list<Matrix> line_bbox;
+
+  // The current horizontal alignment. This is used to align multi-line text.
+  int halign;
+
+  // The X offset for the next glyph.
+  int xoffset;
+
+  // The Y offset of the baseline for the current line.
+  int 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 yoffset;
+
+  // The current mode of the rendering process (box computing or rendering).
+  int mode;
+
+  // The base color of the rendered text.
+  uint8NDArray color;
+
+  // A list of parsed strings to be used for printing.
+  std::list<text_renderer::string> strlist;
+
+  // The X offset of the baseline for the current line.
+  int line_xoffset;
+
+};
+
+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
+
+  font = ft_font (name, weight, angle, size, 0);
+}
+
+void
+ft_text_renderer::push_new_line (void)
+{
+  switch (mode)
+    {
+    case MODE_BBOX:
+      {
+        // Create a new bbox entry based on the current font.
+
+        FT_Face face = font.get_face ();
+
+        if (face)
+          {
+            int asc = face->size->metrics.ascender >> 6;
+            int desc = face->size->metrics.descender >> 6;
+            int h = face->size->metrics.height >> 6;
+
+            Matrix bb (1, 5, 0.0);
+
+            bb(1) = desc;
+            bb(3) = asc - desc;
+            bb(4) = h;
+
+            line_bbox.push_back (bb);
+
+            xoffset = yoffset = 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 = line_bbox.front ();
+        line_bbox.pop_front ();
+        Matrix new_bbox = line_bbox.front ();
+
+        xoffset = line_xoffset = compute_line_xoffset (new_bbox);
+        line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3)));
+        yoffset = 0;
+      }
+      break;
+    }
+}
+
+int
+ft_text_renderer::compute_line_xoffset (const Matrix& lb) const
+{
+  if (! bbox.is_empty ())
+    {
+      switch (halign)
+        {
+        case 0:
+          return 0;
+        case 1:
+          return (bbox(2) - lb(2)) / 2;
+        case 2:
+          return (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.
+
+  bbox = Matrix ();
+
+  switch (line_bbox.size ())
+    {
+    case 0:
+      break;
+
+    case 1:
+      bbox = line_bbox.front ().extract (0, 0, 0, 3);
+      break;
+
+    default:
+      for (std::list<Matrix>::const_iterator it = line_bbox.begin ();
+           it != line_bbox.end (); ++it)
+        {
+          if (bbox.is_empty ())
+            bbox = it->extract (0, 0, 0, 3);
+          else
+            {
+              bbox(1) -= (*it)(3);
+              bbox(3) += (*it)(3);
+              bbox(2) = xmax (bbox(2), (*it)(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 (mode == MODE_BBOX)
+    {
+      int asc = font.get_face ()->size->metrics.ascender >> 6;
+      int desc = font.get_face ()->size->metrics.descender >> 6;
+
+      Matrix& bb = line_bbox.back ();
+
+      if ((yoffset + desc) < bb(1))
+        {
+          // The new font goes below the bottom of the current bbox.
+
+          int delta = bb(1) - (yoffset + desc);
+
+          bb(1) -= delta;
+          bb(3) += delta;
+        }
+
+      if ((yoffset + asc) > (bb(1) + bb(3)))
+        {
+          // The new font goes above the top of the current bbox.
+
+          int delta = (yoffset + asc) - (bb(1) + bb(3));
+
+          bb(3) += delta;
+        }
+    }
+}
+
+void
+ft_text_renderer::set_mode (int m)
+{
+  mode = m;
+
+  switch (mode)
+    {
+    case MODE_BBOX:
+      xoffset = line_yoffset = yoffset = 0;
+      bbox = Matrix (1, 4, 0.0);
+      line_bbox.clear ();
+      push_new_line ();
+      break;
+
+    case MODE_RENDER:
+      if (bbox.numel () != 4)
+        {
+          ::warning ("ft_text_renderer: invalid bounding box, cannot render");
+
+          xoffset = line_yoffset = yoffset = 0;
+          pixels = uint8NDArray ();
+        }
+      else
+        {
+          pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)),
+                                 static_cast<uint8_t> (0));
+          xoffset = compute_line_xoffset (line_bbox.front ());
+          line_yoffset = -bbox(1)-1;
+          yoffset = 0;
+        }
+      break;
+
+    default:
+      error ("ft_text_renderer: invalid mode '%d'", mode);
+      break;
+    }
+}
+
+FT_UInt
+ft_text_renderer::process_character (FT_ULong code, FT_UInt previous)
+{
+  FT_Face face = font.get_face ();
+  FT_UInt glyph_index = 0;
+
+  if (face)
+    {
+      glyph_index = FT_Get_Char_Index (face, code);
+
+      if (code != '\n'
+          && (! glyph_index
+              || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
+        {
+          glyph_index = 0;
+          warn_missing_glyph (code);
+        }
+      else
+        {
+          switch (mode)
+            {
+            case MODE_RENDER:
+              if (code == '\n')
+                {
+                  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
+                    push_new_line ();
+                }
+              else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
+                {
+                  glyph_index = 0;
+                  warn_glyph_render (code);
+                }
+              else
+                {
+                  FT_Bitmap& bitmap = face->glyph->bitmap;
+                  int x0, y0;
+
+                  if (previous)
+                    {
+                      FT_Vector delta;
+
+                      FT_Get_Kerning (face, previous, glyph_index,
+                                      FT_KERNING_DEFAULT, &delta);
+                      xoffset += (delta.x >> 6);
+                    }
+
+                  x0 = xoffset + face->glyph->bitmap_left;
+                  y0 = line_yoffset + yoffset + face->glyph->bitmap_top;
+
+                  // 'w' seems to have a negative -1
+                  // face->glyph->bitmap_left, this is so we don't
+                  // index out of bound, and assumes we we 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++)
+                      {
+                        unsigned char pix = bitmap.buffer[r*bitmap.width+c];
+                        if (x0+c < 0 || x0+c >= pixels.dim2 ()
+                            || y0-r < 0 || y0-r >= pixels.dim3 ())
+                          {
+                            //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)",
+                            //           str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ());
+                          }
+                        else if (pixels(3, x0+c, y0-r).value () == 0)
+                          {
+                            pixels(0, x0+c, y0-r) = color(0);
+                            pixels(1, x0+c, y0-r) = color(1);
+                            pixels(2, x0+c, y0-r) = color(2);
+                            pixels(3, x0+c, y0-r) = pix;
+                          }
+                      }
+
+                  xoffset += (face->glyph->advance.x >> 6);
+                }
+              break;
+
+            case MODE_BBOX:
+              if (code == '\n')
+                {
+                  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
+                    push_new_line ();
+                }
+              else
+                {
+                  Matrix& bb = 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);
+
+                      xoffset += (delta.x >> 6);
+                    }
+
+                  // Extend current X offset box by the width of the current
+                  // glyph. Then extend the line bounding box if necessary.
+
+                  xoffset += (face->glyph->advance.x >> 6);
+                  bb(2) = xmax (bb(2), xoffset);
+                }
+              break;
+            }
+        }
+    }
+
+  return glyph_index;
+}
+
+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;
+
+  // First run text_to_pixels which will also build the string list
+
+  text_to_pixels (txt, pxls, box, ha, va, rot, interp, false);
+
+  lst = strlist;
+}
+
+void
+ft_text_renderer::visit (text_element_string& e)
+{
+  if (font.is_valid ())
+    {
+      FT_UInt glyph_index, previous = 0;
+
+      std::string str = e.string_value ();
+      size_t n = str.length ();
+      size_t curr = 0;
+      size_t idx = 0;
+      mbstate_t ps;
+      memset (&ps, 0, sizeof (ps));  // Initialize state to 0.
+      wchar_t wc;
+
+      text_renderer::string fs (str, font, xoffset, yoffset);
+
+      while (n > 0)
+        {
+          size_t r = gnulib::mbrtowc (&wc, str.data () + curr, n, &ps);
+
+          if (r > 0
+              && r != static_cast<size_t> (-1)
+              && r != static_cast<size_t> (-2))
+            {
+              n -= r;
+              curr += r;
+
+              if (wc == L'\n')
+                {
+                  // Finish previous string in srtlist before processing
+                  // the newline character
+                  fs.set_y (line_yoffset + yoffset);
+                  fs.set_color (color);
+                  std::string s = str.substr (idx, curr - idx - 1);
+                  if (! s.empty ())
+                    {
+                      fs.set_string (s);
+                      strlist.push_back (fs);
+                    }
+                }
+
+              glyph_index = process_character (wc, previous);
+
+              if (wc == L'\n')
+                {
+                  previous = 0;
+                  // Start a new string in strlist
+                  idx = curr;
+                  fs = text_renderer::string (str.substr (idx), font,
+                                             line_xoffset, yoffset);
+
+                }
+              else
+                previous = glyph_index;
+            }
+          else
+            {
+              if (r != 0)
+                ::warning ("ft_text_renderer: failed to decode string `%s' with "
+                           "locale `%s'", str.c_str (),
+                           std::setlocale (LC_CTYPE, 0));
+              break;
+            }
+        }
+
+      if (! fs.get_string ().empty ())
+        {
+          fs.set_y (line_yoffset + yoffset);
+          fs.set_color (color);
+          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.
+
+  ft_font saved_font (font);
+  uint8NDArray saved_color (color);
+
+  text_processor::visit (e);
+
+  font = saved_font;
+  color = saved_color;
+}
+
+void
+ft_text_renderer::visit (text_element_subscript& e)
+{
+  ft_font saved_font (font);
+  int saved_line_yoffset = line_yoffset;
+  int saved_yoffset = yoffset;
+
+  set_font (font.get_name (), font.get_weight (), font.get_angle (),
+            font.get_size () - 2);
+
+  if (font.is_valid ())
+    {
+      int h = font.get_face ()->size->metrics.height >> 6;
+
+      // Shifting the baseline by 2/3 the font height seems to produce
+      // decent result.
+      yoffset -= (h * 2) / 3;
+
+      if (mode == MODE_BBOX)
+        update_line_bbox ();
+    }
+
+  text_processor::visit (e);
+
+  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 (line_yoffset == saved_line_yoffset)
+    yoffset = saved_yoffset;
+}
+
+void
+ft_text_renderer::visit (text_element_superscript& e)
+{
+  ft_font saved_font (font);
+  int saved_line_yoffset = line_yoffset;
+  int saved_yoffset = yoffset;
+
+  set_font (font.get_name (), font.get_weight (), font.get_angle (),
+            font.get_size () - 2);
+
+  if (saved_font.is_valid ())
+    {
+      int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6;
+
+      // Shifting the baseline by 2/3 base font ascender seems to produce
+      // decent result.
+      yoffset += (s_asc * 2) / 3;
+
+      if (mode == MODE_BBOX)
+        update_line_bbox ();
+    }
+
+  text_processor::visit (e);
+
+  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 (line_yoffset == saved_line_yoffset)
+    yoffset = saved_yoffset;
+}
+
+void
+ft_text_renderer::visit (text_element_color& e)
+{
+  if (mode == MODE_RENDER)
+    set_color (e.get_color ());
+}
+
+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.
+
+  set_font (font.get_name (), font.get_weight (), font.get_angle (), sz);
+
+  if (mode == MODE_BBOX)
+    update_line_bbox ();
+}
+
+void
+ft_text_renderer::visit (text_element_fontname& e)
+{
+  set_font (e.get_fontname (), font.get_weight (), font.get_angle (),
+            font.get_size ());
+
+  if (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 (font.get_name (), "normal", "normal", font.get_size ());
+      break;
+
+    case text_element_fontstyle::bold:
+      set_font (font.get_name (), "bold", "normal", font.get_size ());
+      break;
+
+    case text_element_fontstyle::italic:
+      set_font (font.get_name (), "normal", "italic", font.get_size ());
+      break;
+
+    case text_element_fontstyle::oblique:
+      set_font (font.get_name (), "normal", "oblique", font.get_size ());
+      break;
+    }
+
+  if (mode == MODE_BBOX)
+    update_line_bbox ();
+}
+
+void
+ft_text_renderer::visit (text_element_symbol& e)
+{
+  uint32_t code = e.get_symbol_code ();
+
+  text_renderer::string fs (std::string ("-"), font, xoffset, yoffset);
+
+  if (code != text_element_symbol::invalid_code && font.is_valid ())
+    {
+      process_character (code);
+      fs.set_code (code);
+    }
+  else if (font.is_valid ())
+    ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
+
+  if (fs.get_code ())
+    {
+      fs.set_y (line_yoffset + yoffset);
+      fs.set_color (color);
+      strlist.push_back (fs);
+   }
+}
+
+void
+ft_text_renderer::visit (text_element_combined& e)
+{
+  int saved_xoffset = xoffset;
+  int max_xoffset = xoffset;
+
+  for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it)
+    {
+      xoffset = saved_xoffset;
+      (*it)->accept (*this);
+      max_xoffset = xmax (xoffset, max_xoffset);
+    }
+
+  xoffset = max_xoffset;
+}
+
+void
+ft_text_renderer::reset (void)
+{
+  set_mode (MODE_BBOX);
+  set_color (Matrix (1, 3, 0.0));
+}
+
+void
+ft_text_renderer::set_color (const Matrix& c)
+{
+  if (c.numel () == 3)
+    {
+      color(0) = static_cast<uint8_t> (c(0)*255);
+      color(1) = static_cast<uint8_t> (c(1)*255);
+      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 = bbox;
+
+  set_mode (MODE_RENDER);
+  // Clear the list of parsed strings
+  strlist.clear ();
+
+  if (pixels.numel () > 0)
+    {
+      elt->accept (*this);
+
+      switch (rotation)
+        {
+        case ROTATION_0:
+          break;
+
+        case ROTATION_90:
+          {
+            Array<octave_idx_type> perm (dim_vector (3, 1));
+            perm(0) = 0;
+            perm(1) = 2;
+            perm(2) = 1;
+            pixels = pixels.permute (perm);
+
+            Array<idx_vector> idx (dim_vector (3, 1));
+            idx(0) = idx_vector (':');
+            idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
+            idx(2) = idx_vector (':');
+            pixels = uint8NDArray (pixels.index (idx));
+          }
+          break;
+
+        case ROTATION_180:
+          {
+            Array<idx_vector> idx (dim_vector (3, 1));
+            idx(0) = idx_vector (':');
+            idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
+            idx(2)=  idx_vector (pixels.dim3 ()-1, -1, -1);
+            pixels = uint8NDArray (pixels.index (idx));
+          }
+          break;
+
+        case ROTATION_270:
+          {
+            Array<octave_idx_type> perm (dim_vector (3, 1));
+            perm(0) = 0;
+            perm(1) = 2;
+            perm(2) = 1;
+            pixels = pixels.permute (perm);
+
+            Array<idx_vector> idx (dim_vector (3, 1));
+            idx(0) = idx_vector (':');
+            idx(1) = idx_vector (':');
+            idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
+            pixels = uint8NDArray (pixels.index (idx));
+          }
+          break;
+        }
+    }
+
+  return 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.
+
+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);
+
+  switch (rotation_to_mode (rotation))
+    {
+    case ROTATION_0:
+    case ROTATION_180:
+      extent(0) = bbox(2);
+      extent(1) = bbox(3);
+      break;
+
+    case ROTATION_90:
+    case ROTATION_270:
+      extent(0) = bbox(3);
+      extent(1) = bbox(2);
+    }
+
+  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;
+
+  return extent;
+}
+
+int
+ft_text_renderer::rotation_to_mode (double rotation) const
+{
+  // Clip rotation to range [0, 360]
+  while (rotation < 0)
+    rotation += 360.0;
+  while (rotation > 360.0)
+    rotation -= 360.0;
+
+  if (rotation == 0.0)
+    return ROTATION_0;
+  else if (rotation == 90.0)
+    return ROTATION_90;
+  else if (rotation == 180.0)
+    return ROTATION_180;
+  else if (rotation == 270.0)
+    return ROTATION_270;
+  else
+    return ROTATION_0;
+}
+
+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);
+
+  halign = _halign;
+
+  text_element *elt = text_parser::parse (txt, interpreter);
+  pxls = render (elt, box, rot_mode);
+  delete elt;
+
+  if (pxls.is_empty ())
+    return;  // nothing to render
+
+  switch (halign)
+    {
+    case 1:
+      box(0) = -box(2)/2;
+      break;
+
+    case 2:
+      box(0) = -box(2);
+      break;
+
+    default:
+      box(0) = 0;
+      break;
+    }
+
+  switch (valign)
+    {
+    case 1:
+      box(1) = -box(3)/2;
+      break;
+
+    case 2:
+      box(1) = -box(3);
+      break;
+
+    case 3:
+      break;
+
+    case 4:
+      box(1) = -box(3)-box(1);
+      break;
+
+    default:
+      box(1) = 0;
+      break;
+    }
+
+  if (handle_rotation)
+    {
+      switch (rot_mode)
+        {
+        case ROTATION_90:
+          std::swap (box(0), box(1));
+          std::swap (box(2), box(3));
+          box(0) = -box(0)-box(2);
+          break;
+
+        case ROTATION_180:
+          box(0) = -box(0)-box(2);
+          box(1) = -box(1)-box(3);
+          break;
+
+        case ROTATION_270:
+          std::swap (box(0), box(1));
+          std::swap (box(2), box(3));
+          box(1) = -box(1)-box(3);
+          break;
+        }
+    }
+}
+
+ft_text_renderer::ft_font::ft_font (const ft_font& ft)
+  : text_renderer::font (ft), face (0)
+{
+#if defined (HAVE_FT_REFERENCE_FACE)
+  FT_Face ft_face = ft.get_face ();
+
+  if (ft_face && FT_Reference_Face (ft_face) == 0)
+    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);
+
+      if (face)
+        {
+          FT_Done_Face (face);
+          face = 0;
+        }
+
+#if defined (HAVE_FT_REFERENCE_FACE)
+      FT_Face ft_face = ft.get_face ();
+
+      if (ft_face && FT_Reference_Face (ft_face) == 0)
+        face = ft_face;
+#endif
+    }
+
+  return *this;
+}
+
+FT_Face
+ft_text_renderer::ft_font::get_face (void) const
+{
+  if (! face && ! name.empty ())
+    {
+      face = ft_manager::get_font (name, weight, angle, size);
+
+      if (face)
+        {
+          if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
+            ::warning ("ft_text_renderer: unable to set font size to %g", size);
+        }
+      else
+        ::warning ("ft_text_renderer: unable to load appropriate font");
+    }
+
+  return face;
+}
+
+#endif
+
+base_text_renderer *
+make_ft_text_renderer (void)
+{
+#if defined (HAVE_FREETYPE)
+  return new ft_text_renderer ();
+#else
+  return 0;
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/ft-text-renderer.h	Sat Feb 06 08:15:53 2016 -0500
@@ -0,0 +1,31 @@
+/*
+
+Copyright (C) 2016 John W. Eaton
+Copyright (C) 2009-2015 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_ft_text_renderer_h)
+#define octave_ft_text_renderer_h 1
+
+class base_text_renderer;
+
+extern base_text_renderer *make_ft_text_renderer (void);
+
+#endif
--- a/libinterp/corefcn/gl-render.cc	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/gl-render.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -40,8 +40,7 @@
 #include "errwarn.h"
 #include "gl-render.h"
 #include "oct-opengl.h"
-#include "txt-eng.h"
-#include "txt-eng-ft.h"
+#include "text-renderer.h"
 
 #define LIGHT_MODE GL_FRONT_AND_BACK
 
@@ -2897,20 +2896,17 @@
 opengl_renderer::set_color (const Matrix& c)
 {
   glColor3dv (c.data ());
-#if HAVE_FREETYPE
-  text_renderer.set_color (c);
-#endif
+
+  txt_renderer.set_color (c);
 }
 
 void
 opengl_renderer::set_font (const base_properties& props)
 {
-#if HAVE_FREETYPE
-  text_renderer.set_font (props.get ("fontname").string_value (),
-                          props.get ("fontweight").string_value (),
-                          props.get ("fontangle").string_value (),
-                          props.get ("fontsize_points").double_value ());
-#endif
+  txt_renderer.set_font (props.get ("fontname").string_value (),
+                         props.get ("fontweight").string_value (),
+                         props.get ("fontangle").string_value (),
+                         props.get ("fontsize_points").double_value ());
 }
 
 void
@@ -3238,22 +3234,18 @@
                                  Matrix& bbox,
                                  int halign, int valign, double rotation)
 {
-#if HAVE_FREETYPE
-  text_renderer.text_to_pixels (txt, pixels, bbox,
-                                halign, valign, rotation, interpreter);
-#endif
+  txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign,
+                               rotation, interpreter);
 }
 
 void
 opengl_renderer::text_to_strlist (const std::string& txt,
-                                  std::list<ft_render::ft_string>& lst,
+                                  std::list<text_renderer::string>& lst,
                                   Matrix& bbox,
                                   int halign, int valign, double rotation)
 {
-#if HAVE_FREETYPE
-  text_renderer.text_to_strlist (txt, lst, bbox,
-                                 halign, valign, rotation, interpreter);
-#endif
+  txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign,
+                                rotation, interpreter);
 }
 
 Matrix
@@ -3261,32 +3253,31 @@
                               double x, double y, double z,
                               int halign, int valign, double rotation)
 {
-#if HAVE_FREETYPE
+  Matrix bbox (1, 4, 0.0);
+
   if (txt.empty ())
-    return Matrix (1, 4, 0.0);
-
-  uint8NDArray pixels;
-  Matrix bbox;
-  text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
-
-  bool blend = glIsEnabled (GL_BLEND);
-
-  glEnable (GL_BLEND);
-  glEnable (GL_ALPHA_TEST);
-  glRasterPos3d (x, y, z);
-  glBitmap(0, 0, 0, 0, bbox(0), bbox(1), 0);
-  glDrawPixels (bbox(2), bbox(3),
-                GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
-  glDisable (GL_ALPHA_TEST);
-  if (! blend)
-    glDisable (GL_BLEND);
+    return bbox;
+
+  if (txt_renderer.ok ())
+    {
+      uint8NDArray pixels;
+      text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
+
+      bool blend = glIsEnabled (GL_BLEND);
+
+      glEnable (GL_BLEND);
+      glEnable (GL_ALPHA_TEST);
+      glRasterPos3d (x, y, z);
+      glBitmap(0, 0, 0, 0, bbox(0), bbox(1), 0);
+      glDrawPixels (bbox(2), bbox(3),
+                    GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
+      glDisable (GL_ALPHA_TEST);
+
+      if (! blend)
+        glDisable (GL_BLEND);
+    }
 
   return bbox;
-#else
-  warn_disabled_feature ("opengl_renderer::render_text",
-                         "rendering text (FreeType)");
-  return Matrix (1, 4, 0.0);
-#endif
 }
 
 #endif
--- a/libinterp/corefcn/gl-render.h	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/gl-render.h	Sat Feb 06 08:15:53 2016 -0500
@@ -25,7 +25,7 @@
 
 #include "graphics.h"
 #include "oct-opengl.h"
-#include "txt-eng-ft.h"
+#include "text-renderer.h"
 
 #if defined (HAVE_OPENGL)
 
@@ -37,10 +37,7 @@
   opengl_renderer (void)
     : toolkit (), xform (), xmin (), xmax (), ymin (), ymax (),
     zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (),
-    camera_pos (), camera_dir (), interpreter ("none")
-#if HAVE_FREETYPE
-    , text_renderer ()
-#endif
+    camera_pos (), camera_dir (), interpreter ("none"), txt_renderer ()
   { }
 
   virtual ~opengl_renderer (void) { }
@@ -105,7 +102,7 @@
                                double rotation = 0.0);
 
   virtual void text_to_strlist (const std::string& txt,
-                                std::list<ft_render::ft_string>& lst,
+                                std::list<text_renderer::string>& lst,
                                 Matrix& bbox,
                                 int halign = 0, int valign = 0,
                                 double rotation = 0.0);
@@ -195,10 +192,7 @@
   // interpreter to be used by text_to_pixels
   caseless_str interpreter;
 
-#if HAVE_FREETYPE
-  // FreeType render, used for text rendering
-  ft_render text_renderer;
-#endif
+  text_renderer txt_renderer;
 
 private:
   class patch_tesselator;
--- a/libinterp/corefcn/gl2ps-print.cc	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/gl2ps-print.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -41,7 +41,7 @@
 #include "gl-render.h"
 #include "oct-opengl.h"
 #include "sysdep.h"
-#include "txt-eng-ft.h"
+#include "text-renderer.h"
 
 class
 OCTINTERP_API
@@ -123,11 +123,11 @@
 
 private:
 
-  // Use xform to compute the coordinates of the ft_string list
+  // Use xform to compute the coordinates of the string list
   // that have been parsed by freetype
   void fix_strlist_position (double x, double y, double z,
                              Matrix box, double rotation,
-                             std::list<ft_render::ft_string>& lst);
+                             std::list<text_renderer::string>& lst);
 
   int alignment_to_mode (int ha, int va) const;
   FILE *fp;
@@ -302,9 +302,9 @@
 void
 gl2ps_renderer::fix_strlist_position (double x, double y, double z,
                                      Matrix box, double rotation,
-                                     std::list<ft_render::ft_string>& lst)
+                                     std::list<text_renderer::string>& lst)
 {
-  for (std::list<ft_render::ft_string>::iterator p = lst.begin ();
+  for (std::list<text_renderer::string>::iterator p = lst.begin ();
        p != lst.end (); p++)
     {
       // Get pixel coordinates
@@ -552,7 +552,7 @@
   // string using freetype
   Matrix bbox;
   std::string str = txt;
-  std::list<ft_render::ft_string> lst;
+  std::list<text_renderer::string> lst;
 
   text_to_strlist (str, lst, bbox, ha, va, rotation);
 
@@ -565,7 +565,7 @@
       int sz = fontsize;
       if (! lst.empty () && term.find ("tex") == std::string::npos)
         {
-          ft_render::ft_string s = lst.front ();
+          text_renderer::string s = lst.front ();
           name = select_font (s.get_name (), s.get_weight () == "bold",
                               s.get_angle () == "italic");
           set_color (s.get_color ());
@@ -591,7 +591,7 @@
   // Translate and rotate coordinates in order to use bottom-left alignment
   fix_strlist_position (x, y, z, bbox, rotation, lst);
 
-  for (std::list<ft_render::ft_string>::iterator p = lst.begin ();
+  for (std::list<text_renderer::string>::iterator p = lst.begin ();
        p != lst.end (); p++)
     {
       fontname = select_font ((*p).get_name (),
--- a/libinterp/corefcn/graphics.cc	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/graphics.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -54,8 +54,8 @@
 #include "ov-fcn-handle.h"
 #include "pager.h"
 #include "parse.h"
+#include "text-renderer.h"
 #include "toplev.h"
-#include "txt-eng-ft.h"
 #include "unwind-prot.h"
 #include "utils.h"
 #include "octave-default-image.h"
@@ -6167,14 +6167,10 @@
 void
 axes::properties::update_font (void)
 {
-#ifdef HAVE_FREETYPE
-#  ifdef HAVE_FONTCONFIG
-  text_renderer.set_font (get ("fontname").string_value (),
-                          get ("fontweight").string_value (),
-                          get ("fontangle").string_value (),
-                          get ("fontsize_points").double_value ());
-#endif
-#endif
+  txt_renderer.set_font (get ("fontname").string_value (),
+                         get ("fontweight").string_value (),
+                         get ("fontangle").string_value (),
+                         get ("fontsize_points").double_value ());
 }
 
 // The INTERNAL flag defines whether position or outerposition is used.
@@ -6975,18 +6971,24 @@
           std::string label (ticklabels(i));
           label.erase (0, label.find_first_not_of (" "));
           label = label.substr (0, label.find_last_not_of (" ")+1);
-#ifdef HAVE_FREETYPE
-          ext = text_renderer.get_extent (label, 0.0,
-                                          get_ticklabelinterpreter ());
-          wmax = std::max (wmax, ext(0));
-          hmax = std::max (hmax, ext(1));
-#else
-          // FIXME: find a better approximation
-          double fsize = get ("fontsize").double_value ();
-          int len = label.length ();
-          wmax = std::max (wmax, 0.5*fsize*len);
-          hmax = fsize;
-#endif
+
+          if (txt_renderer.ok ())
+            {
+              ext = txt_renderer.get_extent (label, 0.0,
+                                             get_ticklabelinterpreter ());
+
+              wmax = std::max (wmax, ext(0));
+              hmax = std::max (hmax, ext(1));
+            }
+          else
+            {
+              // FIXME: find a better approximation
+              double fsize = get ("fontsize").double_value ();
+              int len = label.length ();
+
+              wmax = std::max (wmax, 0.5*fsize*len);
+              hmax = fsize;
+            }
         }
     }
 
@@ -7940,22 +7942,17 @@
 void
 text::properties::update_font (void)
 {
-#ifdef HAVE_FREETYPE
-#  ifdef HAVE_FONTCONFIG
-  renderer.set_font (get ("fontname").string_value (),
-                     get ("fontweight").string_value (),
-                     get ("fontangle").string_value (),
-                     get ("fontsize_points").double_value ());
-#endif
-  renderer.set_color (get_color_rgb ());
-#endif
+  txt_renderer.set_font (get ("fontname").string_value (),
+                         get ("fontweight").string_value (),
+                         get ("fontangle").string_value (),
+                         get ("fontsize_points").double_value ());
+
+  txt_renderer.set_color (get_color_rgb ());
 }
 
 void
 text::properties::update_text_extent (void)
 {
-#ifdef HAVE_FREETYPE
-
   int halign = 0;
   int valign = 0;
 
@@ -7981,17 +7978,15 @@
 
   string_vector sv = string_prop.string_vector_value ();
 
-  renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
-                           halign, valign, get_rotation (),
-                           get_interpreter ());
+  txt_renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
+                               halign, valign, get_rotation (),
+                               get_interpreter ());
   // The bbox is relative to the text's position.  We'll leave it that
   // way, because get_position does not return valid results when the
   // text is first constructed.  Conversion to proper coordinates is
   // performed in get_extent.
   set_extent (bbox);
 
-#endif
-
   if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel")
       || autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
     update_autopos ("sync");
@@ -8673,23 +8668,20 @@
 void
 uicontrol::properties::update_text_extent (void)
 {
-#ifdef HAVE_FREETYPE
-
   text_element *elt;
-  ft_render text_renderer;
+  text_renderer txt_renderer;
   Matrix box;
 
   // FIXME: parsed content should be cached for efficiency
   // FIXME: support multiline text
 
   elt = text_parser::parse (get_string_string (), "none");
-#ifdef HAVE_FONTCONFIG
-  text_renderer.set_font (get_fontname (),
-                          get_fontweight (),
-                          get_fontangle (),
-                          get_fontsize ());
-#endif
-  box = text_renderer.get_extent (elt, 0);
+
+  txt_renderer.set_font (get_fontname (), get_fontweight (),
+                         get_fontangle (), get_fontsize ());
+
+  box = txt_renderer.get_extent (elt, 0);
+
   delete elt;
 
   Matrix ext (1, 4);
@@ -8701,8 +8693,6 @@
   ext(3) = box(1);
 
   set_extent (ext);
-
-#endif
 }
 
 void
--- a/libinterp/corefcn/graphics.in.h	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/graphics.in.h	Sat Feb 06 08:15:53 2016 -0500
@@ -44,7 +44,7 @@
 #include "oct-mutex.h"
 #include "oct-refcount.h"
 #include "ov.h"
-#include "txt-eng-ft.h"
+#include "text-renderer.h"
 
 // FIXME: maybe this should be a configure option?
 // Matlab defaults to "Helvetica", but that causes problems for many
@@ -3792,10 +3792,8 @@
     bool x2Dtop, y2Dright, layer2Dtop, is2D;
     bool xySym, xyzSym, zSign, nearhoriz;
 
-#if HAVE_FREETYPE
-    // FreeType renderer, used for calculation of text (tick labels) size
-    ft_render text_renderer;
-#endif
+    // Text renderer, used for calculation of text (tick labels) size
+    text_renderer txt_renderer;
 
     void set_text_child (handle_property& h, const std::string& who,
                          const octave_value& v);
@@ -4511,10 +4509,9 @@
     Matrix get_data_position (void) const;
     Matrix get_extent_matrix (void) const;
     const uint8NDArray& get_pixels (void) const { return pixels; }
-#if HAVE_FREETYPE
-    // FreeType renderer, used for calculation of text size
-    ft_render renderer;
-#endif
+
+    // Text renderer, used for calculation of text size
+    text_renderer txt_renderer;
 
   protected:
     void init (void)
--- a/libinterp/corefcn/module.mk	Fri Feb 05 14:53:10 2016 -0500
+++ b/libinterp/corefcn/module.mk	Sat Feb 06 08:15:53 2016 -0500
@@ -21,6 +21,7 @@
   libinterp/corefcn/pt-jit.h
 
 COREFCN_INC = \
+  libinterp/corefcn/base-text-renderer.h \
   libinterp/corefcn/Cell.h \
   libinterp/corefcn/c-file-ptr-stream.h \
   libinterp/corefcn/cdisplay.h \
@@ -37,6 +38,7 @@
   libinterp/corefcn/errwarn.h \
   libinterp/corefcn/event-queue.h \
   libinterp/corefcn/file-io.h \
+  libinterp/corefcn/ft-text-renderer.h \
   libinterp/corefcn/gl-render.h \
   libinterp/corefcn/gl2ps-print.h \
   libinterp/corefcn/gripes.h \
@@ -85,8 +87,8 @@
   libinterp/corefcn/sparse-xpow.h \
   libinterp/corefcn/symtab.h \
   libinterp/corefcn/sysdep.h \
+  libinterp/corefcn/text-renderer.h \
   libinterp/corefcn/toplev.h \
-  libinterp/corefcn/txt-eng-ft.h \
   libinterp/corefcn/txt-eng.h \
   libinterp/corefcn/utils.h \
   libinterp/corefcn/variables.h \
@@ -155,6 +157,7 @@
   libinterp/corefcn/file-io.cc \
   libinterp/corefcn/filter.cc \
   libinterp/corefcn/find.cc \
+  libinterp/corefcn/ft-text-renderer.cc \
   libinterp/corefcn/gammainc.cc \
   libinterp/corefcn/gcd.cc \
   libinterp/corefcn/getgrent.cc \
@@ -236,10 +239,10 @@
   libinterp/corefcn/syscalls.cc \
   libinterp/corefcn/sysdep.cc \
   libinterp/corefcn/time.cc \
+  libinterp/corefcn/text-renderer.cc \
   libinterp/corefcn/toplev.cc \
   libinterp/corefcn/tril.cc \
   libinterp/corefcn/tsearch.cc \
-  libinterp/corefcn/txt-eng-ft.cc \
   libinterp/corefcn/txt-eng.cc \
   libinterp/corefcn/typecast.cc \
   libinterp/corefcn/urlwrite.cc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/text-renderer.cc	Sat Feb 06 08:15:53 2016 -0500
@@ -0,0 +1,140 @@
+/*
+
+Copyright (C) 2016 John W. Eaton
+Copyright (C) 2009-2015 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "base-text-renderer.h"
+#include "errwarn.h"
+#include "ft-text-renderer.h"
+#include "text-renderer.h"
+
+static base_text_renderer *
+make_text_renderer (void)
+{
+  // Allow the possibility of choosing different text rendering
+  // implementations.
+
+  return make_ft_text_renderer ();
+}
+
+text_renderer::text_renderer (void)
+  : rep (make_text_renderer ())
+{ }
+
+text_renderer::~text_renderer (void)
+{
+  delete rep;
+}
+
+bool
+text_renderer::ok (void) const
+{
+  static bool warned = false;
+
+  if (! rep)
+    {
+      if (! warned)
+        {
+          warn_disabled_feature ("opengl_renderer::render_text",
+                                 "rendering text (FreeType)");
+
+          warned = true;
+        }
+    }
+
+  return rep != 0;
+}
+
+Matrix
+text_renderer::get_extent (text_element *elt, double rotation)
+{
+  static Matrix empty_extent (1, 4, 0.0);
+
+  return ok () ? rep->get_extent (elt, rotation) : empty_extent;
+}
+
+Matrix
+text_renderer::get_extent (const std::string& txt, double rotation,
+                           const caseless_str& interpreter)
+{
+  static Matrix empty_extent (1, 4, 0.0);
+
+  return ok () ? rep->get_extent (txt, rotation, interpreter) : empty_extent;
+}
+
+void
+text_renderer::set_font (const std::string& name, const std::string& weight,
+                         const std::string& angle, double size)
+{
+  if (ok ())
+    rep->set_font (name, weight, angle, size);
+}
+
+void
+text_renderer::set_color (const Matrix& c)
+{
+  if (ok ())
+    rep->set_color (c);
+}
+
+void
+text_renderer::text_to_pixels (const std::string& txt,
+                               uint8NDArray& pxls, Matrix& bbox,
+                               int halign, int valign, double rotation,
+                               const caseless_str& interpreter,
+                               bool handle_rotation)
+{
+  static Matrix empty_bbox (1, 4, 0.0);
+  static uint8NDArray empty_pxls;
+
+  if (ok ())
+    rep->text_to_pixels (txt, pxls, bbox, halign, valign, rotation,
+                         interpreter, handle_rotation);
+  else
+    {
+      bbox = empty_bbox;
+      pxls = empty_pxls;
+    }
+}
+
+void
+text_renderer::text_to_strlist (const std::string& txt,
+                                std::list<text_renderer::string>& lst,
+                                Matrix& bbox, int halign, int valign,
+                                double rotation,
+                                const caseless_str& interpreter)
+{
+  static Matrix empty_bbox (1, 4, 0.0);
+  static std::list<text_renderer::string> empty_lst;
+
+  if (ok ())
+    rep->text_to_strlist (txt, lst, bbox, halign, valign, rotation,
+                          interpreter);
+  else
+    {
+      bbox = empty_bbox;
+      lst = empty_lst;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/text-renderer.h	Sat Feb 06 08:15:53 2016 -0500
@@ -0,0 +1,210 @@
+/*
+
+Copyright (C) 2016 John W. Eaton
+Copyright (C) 2009-2015 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_text_renderer_h)
+#define octave_text_renderer_h 1
+
+#include <list>
+#include <string>
+
+#include "caseless-str.h"
+#include "dMatrix.h"
+#include "uint8NDArray.h"
+
+#include "txt-eng.h"
+
+class base_text_renderer;
+
+class
+OCTINTERP_API
+text_renderer
+{
+public:
+
+  text_renderer (void);
+
+  ~text_renderer (void);
+
+  bool ok (void) const;
+
+  Matrix get_extent (text_element *elt, double rotation = 0.0);
+
+  Matrix get_extent (const std::string& txt, double rotation = 0.0,
+                     const caseless_str& interpreter = "tex");
+
+  void set_font (const std::string& name, const std::string& weight,
+                 const std::string& angle, double size);
+
+  void set_color (const Matrix& c);
+
+  void text_to_pixels (const std::string& txt,
+                       uint8NDArray& pxls, Matrix& bbox,
+                       int halign, int valign, double rotation = 0.0,
+                       const caseless_str& interpreter = "tex",
+                       bool handle_rotation = true);
+
+  class font
+  {
+  public:
+
+    font (void)
+      : name (), weight (), angle (), size (0)
+    { }
+
+    font (const std::string& nm, const std::string& wt,
+          const std::string& ang, double sz)
+      : name (nm), weight (wt), angle (ang), size (sz)
+    { }
+
+    font (const font& ft)
+      : name (ft.name), weight (ft.weight), angle (ft.angle),
+        size (ft.size)
+    { }
+
+    ~font (void) { }
+
+    font& operator = (const font& ft)
+    {
+      if (&ft != this)
+        {
+          name = ft.name;
+          weight = ft.weight;
+          angle = ft.angle;
+          size = ft.size;
+        }
+
+      return *this;
+    }
+
+    std::string get_name (void) const { return name; }
+
+    std::string get_weight (void) const { return weight; }
+
+    std::string get_angle (void) const { return angle; }
+
+    double get_size (void) const { return size; }
+
+  protected:
+
+    std::string name;
+    std::string weight;
+    std::string angle;
+    double size;
+  };
+
+  // Container for substrings after parsing.
+
+  class string
+  {
+  public:
+
+    string (const std::string& s, font& f, const double x0, const double y0)
+      : str (s), fnt (f), x (x0), y (y0), z (0.0), code (0),
+        color (Matrix (1,3,0.0))
+    { }
+
+    string (const string& s)
+      : str (s.str), fnt (s.fnt), x (s.x), y (s.y), code (s.code),
+        color (s.color)
+    { }
+
+    ~string (void) { }
+
+    string& operator = (const string& s)
+    {
+      if (&s != this)
+        {
+          str = s.str;
+          fnt = s.fnt;
+          x = s.x;
+          y = s.y;
+          code = s.code;
+          color = s.color;
+        }
+
+      return *this;
+    }
+
+    void set_string (const std::string& s) { str = s; }
+
+    std::string get_string (void) const { return str; }
+
+    std::string get_name (void) const { return fnt.get_name (); }
+
+    std::string get_weight (void) const { return fnt.get_weight (); }
+
+    std::string get_angle (void) const { return fnt.get_angle (); }
+
+    double get_size (void) const { return fnt.get_size (); }
+
+    void set_x (const double x0) { x = x0; }
+
+    double get_x (void) const { return x; }
+
+    void set_y (const double y0) { y = y0; }
+
+    double get_y (void) const { return y; }
+
+    void set_z (const double z0) { z = z0; }
+
+    double get_z (void) const { return z; }
+
+    void set_code (const uint32_t c) { code = c; }
+
+    uint32_t get_code (void) const { return code; }
+
+    void set_color (const uint8NDArray& c)
+    {
+      color(0) = static_cast<double> (c(0)) / 255;
+      color(1) = static_cast<double> (c(1)) / 255;
+      color(2) = static_cast<double> (c(2)) / 255;
+    }
+
+    Matrix get_color (void) const { return color; }
+
+  private:
+
+    std::string str;
+    font fnt;
+    double x, y, z;
+    uint32_t code;
+    Matrix color;
+  };
+
+  void text_to_strlist (const std::string& txt,
+                        std::list<string>& lst, Matrix& box,
+                        int halign, int valign, double rotation = 0.0,
+                        const caseless_str& interpreter = "tex");
+
+private:
+
+  base_text_renderer *rep;
+
+  // No copying!
+
+  text_renderer (const text_renderer&);
+
+  text_renderer& operator = (const text_renderer&);
+};
+
+#endif
--- a/libinterp/corefcn/txt-eng-ft.cc	Fri Feb 05 14:53:10 2016 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1127 +0,0 @@
-/*
-
-Copyright (C) 2009-2015 Michael Goffioul
-
-This file is part of Octave.
-
-Octave is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 3 of the License, or (at your
-option) any later version.
-
-Octave is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#ifdef HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#if defined (HAVE_FREETYPE)
-
-#if defined (HAVE_FONTCONFIG)
-#  include <fontconfig/fontconfig.h>
-#endif
-
-#include <clocale>
-#include <cwchar>
-#include <iostream>
-#include <map>
-#include <utility>
-
-#include "singleton-cleanup.h"
-
-#include "error.h"
-#include "pr-output.h"
-#include "txt-eng-ft.h"
-
-// 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",
-                   "ft_render: skipping missing glyph for character '%x'", c);
-}
-
-static void
-warn_glyph_render (FT_ULong c)
-{
-  warning_with_id ("Octave:glyph-render",
-                   "ft_render: unable to render glyph for character '%x'", c);
-}
-
-#ifdef _MSC_VER
-// 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);
-
-class
-ft_manager
-{
-public:
-  static bool instance_ok (void)
-  {
-    bool retval = true;
-
-    if (! instance)
-      {
-        instance = new ft_manager ();
-
-        if (instance)
-          singleton_cleanup_list::add (cleanup_instance);
-      }
-
-    if (! instance)
-      error ("unable to create ft_manager!");
-
-    return retval;
-  }
-
-  static void cleanup_instance (void) { delete instance; instance = 0; }
-
-  static FT_Face get_font (const std::string& name, const std::string& weight,
-                           const std::string& angle, double size)
-  {
-    return (instance_ok ()
-            ? instance->do_get_font (name, weight, angle, size)
-            : 0);
-  }
-
-  static void font_destroyed (FT_Face face)
-  {
-    if (instance_ok ())
-      instance->do_font_destroyed (face);
-  }
-
-private:
-
-  static ft_manager *instance;
-
-  typedef std::pair<std::string, double> ft_key;
-  typedef std::map<ft_key, FT_Face> ft_cache;
-
-  // Cache the fonts loaded by FreeType. This cache only contains
-  // weak references to the fonts, strong references are only present
-  // in class ft_render.
-  ft_cache cache;
-
-private:
-
-  // No copying!
-
-  ft_manager (const ft_manager&);
-
-  ft_manager& operator = (const ft_manager&);
-
-  ft_manager (void)
-    : library (), freetype_initialized (false), fontconfig_initialized (false)
-  {
-    if (FT_Init_FreeType (&library))
-      error ("unable to initialize FreeType library");
-    else
-      freetype_initialized = true;
-
-#if defined (HAVE_FONTCONFIG)
-    if (! FcInit ())
-      error ("unable to initialize fontconfig library");
-    else
-      fontconfig_initialized = true;
-#endif
-  }
-
-  ~ft_manager (void)
-  {
-    if (freetype_initialized)
-      FT_Done_FreeType (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 (fontconfig_initialized)
-    //   FcFini ();
-#endif
-  }
-
-
-  FT_Face do_get_font (const std::string& name, const std::string& weight,
-                       const std::string& angle, double size)
-  {
-    FT_Face retval = 0;
-
-#if 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, size);
-    ft_cache::const_iterator it = cache.find (key);
-
-    if (it != cache.end ())
-      {
-        FT_Reference_Face (it->second);
-        return it->second;
-      }
-#endif
-
-    std::string file;
-
-#if defined (HAVE_FONTCONFIG)
-    if (fontconfig_initialized)
-      {
-        int fc_weight, fc_angle;
-
-        if (weight == "bold")
-          fc_weight = FC_WEIGHT_BOLD;
-        else if (weight == "light")
-          fc_weight = FC_WEIGHT_LIGHT;
-        else if (weight == "demi")
-          fc_weight = FC_WEIGHT_DEMIBOLD;
-        else
-          fc_weight = FC_WEIGHT_NORMAL;
-
-        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 == "*" ? "sans" : name.c_str ())));
-
-        FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
-        FcPatternAddInteger (pat, FC_SLANT, fc_angle);
-        FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
-
-        if (FcConfigSubstitute (0, pat, FcMatchPattern))
-          {
-            FcResult res;
-            FcPattern *match;
-
-            FcDefaultSubstitute (pat);
-            match = FcFontMatch (0, 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",
-                         name.c_str (), weight.c_str (), angle.c_str (),
-                         size);
-
-            if (match)
-              FcPatternDestroy (match);
-          }
-
-        FcPatternDestroy (pat);
-      }
-#endif
-
-    if (file.empty ())
-      {
-#ifdef __WIN32__
-        file = "C:/WINDOWS/Fonts/verdana.ttf";
-#else
-        // FIXME: find a "standard" font for UNIX platforms
-#endif
-      }
-
-    if (! file.empty ())
-      {
-        if (FT_New_Face (library, file.c_str (), 0, &retval))
-          ::warning ("ft_manager: unable to load font: %s", file.c_str ());
-#if 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.
-
-            retval->generic.data = new ft_key (key);
-            retval->generic.finalizer = ft_face_destroyed;
-
-            // Insert loaded font into the cache.
-
-            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);
-
-        cache.erase (*pkey);
-        delete pkey;
-        face->generic.data = 0;
-      }
-  }
-
-private:
-  FT_Library library;
-  bool freetype_initialized;
-  bool fontconfig_initialized;
-};
-
-ft_manager* ft_manager::instance = 0;
-
-static void
-ft_face_destroyed (void* object)
-{ ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object)); }
-
-// ---------------------------------------------------------------------------
-
-ft_render::ft_render (void)
-  : text_processor (), font (), bbox (1, 4, 0.0), halign (0), xoffset (0),
-    line_yoffset (0), yoffset (0), mode (MODE_BBOX),
-    color (dim_vector (1, 3), 0)
-{
-}
-
-ft_render::~ft_render (void)
-{
-}
-
-void
-ft_render::set_font (const std::string& name, const std::string& weight,
-                     const std::string& angle, double size)
-{
-  // FIXME: take "fontunits" into account
-
-  font = ft_font (name, weight, angle, size, 0);
-}
-
-void
-ft_render::push_new_line (void)
-{
-  switch (mode)
-    {
-    case MODE_BBOX:
-      {
-        // Create a new bbox entry based on the current font.
-
-        FT_Face face = font.get_face ();
-
-        if (face)
-          {
-            int asc = face->size->metrics.ascender >> 6;
-            int desc = face->size->metrics.descender >> 6;
-            int h = face->size->metrics.height >> 6;
-
-            Matrix bb (1, 5, 0.0);
-
-            bb(1) = desc;
-            bb(3) = asc - desc;
-            bb(4) = h;
-
-            line_bbox.push_back (bb);
-
-            xoffset = yoffset = 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 = line_bbox.front ();
-        line_bbox.pop_front ();
-        Matrix new_bbox = line_bbox.front ();
-
-        xoffset = line_xoffset = compute_line_xoffset (new_bbox);
-        line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3)));
-        yoffset = 0;
-      }
-      break;
-    }
-}
-
-int
-ft_render::compute_line_xoffset (const Matrix& lb) const
-{
-  if (! bbox.is_empty ())
-    {
-      switch (halign)
-        {
-        case 0:
-          return 0;
-        case 1:
-          return (bbox(2) - lb(2)) / 2;
-        case 2:
-          return (bbox(2) - lb(2));
-        }
-    }
-
-  return 0;
-}
-
-void
-ft_render::compute_bbox (void)
-{
-  // Stack the various line bbox together and compute the final
-  // bounding box for the entire text string.
-
-  bbox = Matrix ();
-
-  switch (line_bbox.size ())
-    {
-    case 0:
-      break;
-    case 1:
-      bbox = line_bbox.front ().extract (0, 0, 0, 3);
-      break;
-    default:
-      for (std::list<Matrix>::const_iterator it = line_bbox.begin ();
-           it != line_bbox.end (); ++it)
-        {
-          if (bbox.is_empty ())
-            bbox = it->extract (0, 0, 0, 3);
-          else
-            {
-              bbox(1) -= (*it)(3);
-              bbox(3) += (*it)(3);
-              bbox(2) = xmax (bbox(2), (*it)(2));
-            }
-        }
-      break;
-    }
-}
-
-void
-ft_render::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 (mode == MODE_BBOX)
-    {
-      int asc = font.get_face ()->size->metrics.ascender >> 6;
-      int desc = font.get_face ()->size->metrics.descender >> 6;
-
-      Matrix& bb = line_bbox.back ();
-
-      if ((yoffset + desc) < bb(1))
-        {
-          // The new font goes below the bottom of the current bbox.
-
-          int delta = bb(1) - (yoffset + desc);
-
-          bb(1) -= delta;
-          bb(3) += delta;
-        }
-
-      if ((yoffset + asc) > (bb(1) + bb(3)))
-        {
-          // The new font goes above the top of the current bbox.
-
-          int delta = (yoffset + asc) - (bb(1) + bb(3));
-
-          bb(3) += delta;
-        }
-    }
-}
-
-void
-ft_render::set_mode (int m)
-{
-  mode = m;
-
-  switch (mode)
-    {
-    case MODE_BBOX:
-      xoffset = line_yoffset = yoffset = 0;
-      bbox = Matrix (1, 4, 0.0);
-      line_bbox.clear ();
-      push_new_line ();
-      break;
-    case MODE_RENDER:
-      if (bbox.numel () != 4)
-        {
-          ::warning ("ft_render: invalid bounding box, cannot render");
-
-          xoffset = line_yoffset = yoffset = 0;
-          pixels = uint8NDArray ();
-        }
-      else
-        {
-          pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)),
-                                 static_cast<uint8_t> (0));
-          xoffset = compute_line_xoffset (line_bbox.front ());
-          line_yoffset = -bbox(1)-1;
-          yoffset = 0;
-        }
-      break;
-    default:
-      error ("ft_render: invalid mode '%d'", mode);
-      break;
-    }
-}
-
-FT_UInt
-ft_render::process_character (FT_ULong code, FT_UInt previous)
-{
-  FT_Face face = font.get_face ();
-  FT_UInt glyph_index = 0;
-
-  if (face)
-    {
-      glyph_index = FT_Get_Char_Index (face, code);
-
-      if (code != '\n'
-          && (! glyph_index
-              || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
-        {
-          glyph_index = 0;
-          warn_missing_glyph (code);
-        }
-      else
-        {
-          switch (mode)
-            {
-            case MODE_RENDER:
-              if (code == '\n')
-                {
-                  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
-                    push_new_line ();
-                }
-              else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
-                {
-                  glyph_index = 0;
-                  warn_glyph_render (code);
-                }
-              else
-                {
-                  FT_Bitmap& bitmap = face->glyph->bitmap;
-                  int x0, y0;
-
-                  if (previous)
-                    {
-                      FT_Vector delta;
-
-                      FT_Get_Kerning (face, previous, glyph_index,
-                                      FT_KERNING_DEFAULT, &delta);
-                      xoffset += (delta.x >> 6);
-                    }
-
-                  x0 = xoffset + face->glyph->bitmap_left;
-                  y0 = line_yoffset + yoffset + face->glyph->bitmap_top;
-
-                  // 'w' seems to have a negative -1
-                  // face->glyph->bitmap_left, this is so we don't
-                  // index out of bound, and assumes we we 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++)
-                      {
-                        unsigned char pix = bitmap.buffer[r*bitmap.width+c];
-                        if (x0+c < 0 || x0+c >= pixels.dim2 ()
-                            || y0-r < 0 || y0-r >= pixels.dim3 ())
-                          {
-                            //::warning ("ft_render: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)",
-                            //           str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ());
-                          }
-                        else if (pixels(3, x0+c, y0-r).value () == 0)
-                          {
-                            pixels(0, x0+c, y0-r) = color(0);
-                            pixels(1, x0+c, y0-r) = color(1);
-                            pixels(2, x0+c, y0-r) = color(2);
-                            pixels(3, x0+c, y0-r) = pix;
-                          }
-                      }
-
-                  xoffset += (face->glyph->advance.x >> 6);
-                }
-              break;
-
-            case MODE_BBOX:
-              if (code == '\n')
-                {
-                  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
-                    push_new_line ();
-                }
-              else
-                {
-                  Matrix& bb = 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);
-
-                      xoffset += (delta.x >> 6);
-                    }
-
-                  // Extend current X offset box by the width of the current
-                  // glyph. Then extend the line bounding box if necessary.
-
-                  xoffset += (face->glyph->advance.x >> 6);
-                  bb(2) = xmax (bb(2), xoffset);
-                }
-              break;
-            }
-        }
-    }
-
-  return glyph_index;
-}
-
-void
-ft_render::visit (text_element_string& e)
-{
-  if (font.is_valid ())
-    {
-      FT_UInt glyph_index, previous = 0;
-
-      std::string str = e.string_value ();
-      size_t n = str.length ();
-      size_t curr = 0;
-      size_t idx = 0;
-      mbstate_t ps;
-      memset (&ps, 0, sizeof (ps));  // Initialize state to 0.
-      wchar_t wc;
-
-      ft_string fs (str, font.get_angle (), font.get_weight (),
-                    font.get_name (), font.get_size (), xoffset, yoffset);
-
-      while (n > 0)
-        {
-          size_t r = gnulib::mbrtowc (&wc, str.data () + curr, n, &ps);
-
-          if (r > 0
-              && r != static_cast<size_t> (-1)
-              && r != static_cast<size_t> (-2))
-            {
-              n -= r;
-              curr += r;
-
-              if (wc == L'\n')
-                {
-                  // Finish previous string in srtlist before processing
-                  // the newline character
-                  fs.set_y (line_yoffset + yoffset);
-                  fs.set_color (color);
-                  std::string s = str.substr (idx, curr - idx - 1);
-                  if (! s.empty ())
-                    {
-                      fs.set_string (s);
-                      strlist.push_back (fs);
-                    }
-                }
-
-              glyph_index = process_character (wc, previous);
-
-              if (wc == L'\n')
-                {
-                  previous = 0;
-                  // Start a new string in strlist
-                  idx = curr;
-                  fs = ft_string (str.substr (idx), font.get_angle (),
-                                  font.get_weight (), font.get_name (),
-                                  font.get_size (), line_xoffset, yoffset);
-
-                }
-              else
-                previous = glyph_index;
-            }
-          else
-            {
-              if (r != 0)
-                ::warning ("ft_render: failed to decode string `%s' with "
-                           "locale `%s'", str.c_str (),
-                           std::setlocale (LC_CTYPE, 0));
-              break;
-            }
-        }
-      if (! fs.get_string ().empty ())
-        {
-          fs.set_y (line_yoffset + yoffset);
-          fs.set_color (color);
-          strlist.push_back (fs);
-        }
-    }
-}
-
-void
-ft_render::visit (text_element_list& e)
-{
-  // Save and restore (after processing the list) the current font and color.
-
-  ft_font saved_font (font);
-  uint8NDArray saved_color (color);
-
-  text_processor::visit (e);
-
-  font = saved_font;
-  color = saved_color;
-}
-
-void
-ft_render::visit (text_element_subscript& e)
-{
-  ft_font saved_font (font);
-  int saved_line_yoffset = line_yoffset;
-  int saved_yoffset = yoffset;
-
-  set_font (font.get_name (), font.get_weight (), font.get_angle (),
-            font.get_size () - 2);
-
-  if (font.is_valid ())
-    {
-      int h = font.get_face ()->size->metrics.height >> 6;
-
-      // Shifting the baseline by 2/3 the font height seems to produce
-      // decent result.
-      yoffset -= (h * 2) / 3;
-
-      if (mode == MODE_BBOX)
-        update_line_bbox ();
-    }
-
-  text_processor::visit (e);
-
-  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 (line_yoffset == saved_line_yoffset)
-    yoffset = saved_yoffset;
-}
-
-void
-ft_render::visit (text_element_superscript& e)
-{
-  ft_font saved_font (font);
-  int saved_line_yoffset = line_yoffset;
-  int saved_yoffset = yoffset;
-
-  set_font (font.get_name (), font.get_weight (), font.get_angle (),
-            font.get_size () - 2);
-
-  if (saved_font.is_valid ())
-    {
-      int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6;
-
-      // Shifting the baseline by 2/3 base font ascender seems to produce
-      // decent result.
-      yoffset += (s_asc * 2) / 3;
-
-      if (mode == MODE_BBOX)
-        update_line_bbox ();
-    }
-
-  text_processor::visit (e);
-
-  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 (line_yoffset == saved_line_yoffset)
-    yoffset = saved_yoffset;
-}
-
-void
-ft_render::visit (text_element_color& e)
-{
-  if (mode == MODE_RENDER)
-    set_color (e.get_color ());
-}
-
-void
-ft_render::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.
-
-  set_font (font.get_name (), font.get_weight (), font.get_angle (), sz);
-
-  if (mode == MODE_BBOX)
-    update_line_bbox ();
-}
-
-void
-ft_render::visit (text_element_fontname& e)
-{
-  set_font (e.get_fontname (), font.get_weight (), font.get_angle (),
-            font.get_size ());
-
-  if (mode == MODE_BBOX)
-    update_line_bbox ();
-}
-
-void
-ft_render::visit (text_element_fontstyle& e)
-{
-  switch (e.get_fontstyle ())
-    {
-    case text_element_fontstyle::normal:
-      set_font (font.get_name (), "normal", "normal", font.get_size ());
-      break;
-    case text_element_fontstyle::bold:
-      set_font (font.get_name (), "bold", "normal", font.get_size ());
-      break;
-    case text_element_fontstyle::italic:
-      set_font (font.get_name (), "normal", "italic", font.get_size ());
-      break;
-    case text_element_fontstyle::oblique:
-      set_font (font.get_name (), "normal", "oblique", font.get_size ());
-      break;
-    }
-
-  if (mode == MODE_BBOX)
-    update_line_bbox ();
-}
-
-void
-ft_render::visit (text_element_symbol& e)
-{
-  uint32_t code = e.get_symbol_code ();
-
-  ft_string fs (std::string ("-"), font.get_angle (), font.get_weight (),
-                font.get_name (), font.get_size (), xoffset, yoffset);
-
-  if (code != text_element_symbol::invalid_code && font.is_valid ())
-    {
-      process_character (code);
-      fs.set_code (code);
-    }
-  else if (font.is_valid ())
-    ::warning ("ignoring unknown symbol: %d", e.get_symbol ());
-
-  if (fs.get_code ())
-    {
-      fs.set_y (line_yoffset + yoffset);
-      fs.set_color (color);
-      strlist.push_back (fs);
-   }
-}
-
-void
-ft_render::visit (text_element_combined& e)
-{
-  int saved_xoffset = xoffset;
-  int max_xoffset = xoffset;
-
-  for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it)
-    {
-      xoffset = saved_xoffset;
-      (*it)->accept (*this);
-      max_xoffset = xmax (xoffset, max_xoffset);
-    }
-
-  xoffset = max_xoffset;
-}
-
-void
-ft_render::reset (void)
-{
-  set_mode (MODE_BBOX);
-  set_color (Matrix (1, 3, 0.0));
-}
-
-void
-ft_render::set_color (Matrix c)
-{
-  if (c.numel () == 3)
-    {
-      color(0) = static_cast<uint8_t> (c(0)*255);
-      color(1) = static_cast<uint8_t> (c(1)*255);
-      color(2) = static_cast<uint8_t> (c(2)*255);
-    }
-  else
-    ::warning ("ft_render::set_color: invalid color");
-}
-
-uint8NDArray
-ft_render::render (text_element* elt, Matrix& box, int rotation)
-{
-  set_mode (MODE_BBOX);
-  elt->accept (*this);
-  compute_bbox ();
-  box = bbox;
-
-  set_mode (MODE_RENDER);
-  // Clear the list of parsed strings
-  strlist.clear ();
-
-  if (pixels.numel () > 0)
-    {
-      elt->accept (*this);
-
-      switch (rotation)
-        {
-        case ROTATION_0:
-          break;
-        case ROTATION_90:
-          {
-            Array<octave_idx_type> perm (dim_vector (3, 1));
-            perm(0) = 0;
-            perm(1) = 2;
-            perm(2) = 1;
-            pixels = pixels.permute (perm);
-
-            Array<idx_vector> idx (dim_vector (3, 1));
-            idx(0) = idx_vector (':');
-            idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
-            idx(2) = idx_vector (':');
-            pixels = uint8NDArray (pixels.index (idx));
-          }
-          break;
-        case ROTATION_180:
-          {
-            Array<idx_vector> idx (dim_vector (3, 1));
-            idx(0) = idx_vector (':');
-            idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
-            idx(2)=  idx_vector (pixels.dim3 ()-1, -1, -1);
-            pixels = uint8NDArray (pixels.index (idx));
-          }
-          break;
-        case ROTATION_270:
-          {
-            Array<octave_idx_type> perm (dim_vector (3, 1));
-            perm(0) = 0;
-            perm(1) = 2;
-            perm(2) = 1;
-            pixels = pixels.permute (perm);
-
-            Array<idx_vector> idx (dim_vector (3, 1));
-            idx(0) = idx_vector (':');
-            idx(1) = idx_vector (':');
-            idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
-            pixels = uint8NDArray (pixels.index (idx));
-          }
-          break;
-        }
-    }
-
-  return 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.
-
-Matrix
-ft_render::get_extent (text_element *elt, double rotation)
-{
-  set_mode (MODE_BBOX);
-  elt->accept (*this);
-  compute_bbox ();
-
-  Matrix extent (1, 2, 0.0);
-
-  switch (rotation_to_mode (rotation))
-    {
-    case ROTATION_0:
-    case ROTATION_180:
-      extent(0) = bbox(2);
-      extent(1) = bbox(3);
-      break;
-    case ROTATION_90:
-    case ROTATION_270:
-      extent(0) = bbox(3);
-      extent(1) = bbox(2);
-    }
-
-  return extent;
-}
-
-Matrix
-ft_render::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;
-}
-
-int
-ft_render::rotation_to_mode (double rotation) const
-{
-  // Clip rotation to range [0, 360]
-  while (rotation < 0)
-    rotation += 360.0;
-  while (rotation > 360.0)
-    rotation -= 360.0;
-
-  if (rotation == 0.0)
-    return ROTATION_0;
-  else if (rotation == 90.0)
-    return ROTATION_90;
-  else if (rotation == 180.0)
-    return ROTATION_180;
-  else if (rotation == 270.0)
-    return ROTATION_270;
-  else
-    return ROTATION_0;
-}
-
-void
-ft_render::text_to_pixels (const std::string& txt,
-                           uint8NDArray& pixels_, Matrix& box,
-                           int _halign, int valign, double rotation,
-                           const caseless_str& interpreter,
-                           bool handle_rotation)
-{
-  int rot_mode = rotation_to_mode (rotation);
-
-  halign = _halign;
-
-  text_element *elt = text_parser::parse (txt, interpreter);
-  pixels_ = render (elt, box, rot_mode);
-  delete elt;
-
-  if (pixels_.is_empty ())
-    return;  // nothing to render
-
-  switch (halign)
-    {
-    default: box(0) = 0; break;
-    case 1: box(0) = -box(2)/2; break;
-    case 2: box(0) = -box(2); break;
-    }
-  switch (valign)
-    {
-    default: box(1) = 0; break;
-    case 1: box(1) = -box(3)/2; break;
-    case 2: box(1) = -box(3); break;
-    case 3: break;
-    case 4: box(1) = -box(3)-box(1); break;
-    }
-
-  if (handle_rotation)
-    switch (rot_mode)
-      {
-      case ROTATION_90:
-        std::swap (box(0), box(1));
-        std::swap (box(2), box(3));
-        box(0) = -box(0)-box(2);
-        break;
-      case ROTATION_180:
-        box(0) = -box(0)-box(2);
-        box(1) = -box(1)-box(3);
-        break;
-      case ROTATION_270:
-        std::swap (box(0), box(1));
-        std::swap (box(2), box(3));
-        box(1) = -box(1)-box(3);
-        break;
-      }
-}
-
-ft_render::ft_font::ft_font (const ft_font& ft)
-  : name (ft.name), weight (ft.weight), angle (ft.angle), size (ft.size),
-    face (0)
-{
-#if HAVE_FT_REFERENCE_FACE
-  FT_Face ft_face = ft.get_face ();
-
-  if (ft_face && FT_Reference_Face (ft_face) == 0)
-    face = ft_face;
-#endif
-}
-
-ft_render::ft_font&
-ft_render::ft_font::operator = (const ft_font& ft)
-{
-  if (&ft != this)
-    {
-      name = ft.name;
-      weight = ft.weight;
-      angle = ft.angle;
-      size = ft.size;
-      if (face)
-        {
-          FT_Done_Face (face);
-          face = 0;
-        }
-
-#if HAVE_FT_REFERENCE_FACE
-      FT_Face ft_face = ft.get_face ();
-
-      if (ft_face && FT_Reference_Face (ft_face) == 0)
-        face = ft_face;
-#endif
-    }
-
-  return *this;
-}
-
-FT_Face
-ft_render::ft_font::get_face (void) const
-{
-  if (! face && ! name.empty ())
-    {
-      face = ft_manager::get_font (name, weight, angle, size);
-
-      if (face)
-        {
-          if (FT_Set_Char_Size (face, 0, size*64, 0, 0))
-            ::warning ("ft_render: unable to set font size to %g", size);
-        }
-      else
-        ::warning ("ft_render: unable to load appropriate font");
-    }
-
-  return face;
-}
-
-#endif
--- a/libinterp/corefcn/txt-eng-ft.h	Fri Feb 05 14:53:10 2016 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,279 +0,0 @@
-/*
-
-Copyright (C) 2009-2015 Michael Goffioul
-
-This file is part of Octave.
-
-Octave is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 3 of the License, or (at your
-option) any later version.
-
-Octave is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#if ! defined (txt_eng_ft_h)
-#define txt_eng_ft_h 1
-
-#if HAVE_FREETYPE
-
-#include <list>
-#include <vector>
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-#include <dMatrix.h>
-#include <uint8NDArray.h>
-#include "txt-eng.h"
-
-class
-OCTINTERP_API
-ft_render : public text_processor
-{
-public:
-  enum
-  {
-    MODE_BBOX   = 0,
-    MODE_RENDER = 1
-  };
-
-  enum
-  {
-    ROTATION_0   = 0,
-    ROTATION_90  = 1,
-    ROTATION_180 = 2,
-    ROTATION_270 = 3
-  };
-
-public:
-
-  ft_render (void);
-
-  ~ft_render (void);
-
-  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 pixels; }
-
-  Matrix get_boundingbox (void) const { return 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 = 0.0,
-                     const caseless_str& interpreter = "tex");
-
-  void set_font (const std::string& name, const std::string& weight,
-                 const std::string& angle, double size);
-
-  void set_color (Matrix c);
-
-  void set_mode (int m);
-
-  void text_to_pixels (const std::string& txt,
-                       uint8NDArray& pixels_, Matrix& bbox,
-                       int halign, int valign, double rotation,
-                       const caseless_str& interpreter = "tex",
-                       bool handle_rotation = true);
-
-private:
-  int rotation_to_mode (double rotation) const;
-
-  // No copying!
-
-  ft_render (const ft_render&);
-
-  ft_render& operator = (const ft_render&);
-
-  // Class to hold information about fonts and a strong
-  // reference to the font objects loaded by FreeType.
-  class ft_font
-  {
-  public:
-    ft_font (void)
-      : name (), weight (), angle (), size (0), face (0) { }
-
-    ft_font (const std::string& nm, const std::string& wt,
-             const std::string& ang, double sz, FT_Face f = 0)
-      : name (nm), weight (wt), angle (ang), size (sz), face (f) { }
-
-    ft_font (const ft_font& ft);
-
-    ~ft_font (void)
-    {
-      if (face)
-        FT_Done_Face (face);
-    }
-
-    ft_font& operator = (const ft_font& ft);
-
-    bool is_valid (void) const { return get_face (); }
-
-    std::string get_name (void) const { return name; }
-
-    std::string get_weight (void) const { return weight; }
-
-    std::string get_angle (void) const { return angle; }
-
-    double get_size (void) const { return size; }
-
-    FT_Face get_face (void) const;
-
-  private:
-    std::string name;
-    std::string weight;
-    std::string angle;
-    double size;
-    mutable FT_Face 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 = 0);
-
-public:
-  // A class to store informations on substrings after parsing.
-  class ft_string : public ft_font
-  {
-  public:
-    ft_string (const std::string s, const std::string fontang,
-              const std::string fontwgt, const std::string nm,
-              const double fontsz, const double x0, const double y0)
-      : ft_font (nm, fontwgt, fontang, fontsz),
-        string(s), x(x0), y(y0), z(0.0), code(0),
-        color(Matrix (1,3,0.0)){ }
-
-    void set_string (const std::string str) { string = str; }
-
-    std::string get_string (void) const { return string; }
-
-    void set_x (const double x0) { x = x0; }
-
-    double get_x (void) const { return x; }
-
-    void set_y (const double y0) { y = y0; }
-
-    double get_y (void) const { return y; }
-
-    void set_z (const double z0) { z = z0; }
-
-    double get_z (void) const { return z; }
-
-    void set_code (const uint32_t c) { code = c; }
-
-    uint32_t get_code (void) const { return code; }
-
-    void set_color (const uint8NDArray c)
-    {
-      color(0) = static_cast<double> (c(0)) / 255;
-      color(1) = static_cast<double> (c(1)) / 255;
-      color(2) = static_cast<double> (c(2)) / 255;
-    }
-
-    Matrix get_color (void) const { return color; }
-
-  private:
-    std::string  string;
-    double x, y, z;
-    uint32_t code;
-    Matrix color;
-  };
-
- void text_to_strlist (const std::string& txt,
-                        std::list<ft_string>& lst, Matrix& box,
-                        int ha, int va, double rot,
-                        const caseless_str& interp = "tex")
-  {
-    uint8NDArray pixels_;
-    // First run text_to_pixels which will also build the string list
-    text_to_pixels (txt, pixels_, box, ha, va, rot, interp, false);
-
-    lst = strlist;
-  }
-
-private:
-  // The current font used by the renderer.
-  ft_font 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 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 pixels;
-
-  // Used to store the bounding box of each line. This is used to layout
-  // multiline text properly.
-  std::list<Matrix> line_bbox;
-
-  // The current horizontal alignment. This is used to align multi-line text.
-  int halign;
-
-  // The X offset for the next glyph.
-  int xoffset;
-
-  // The Y offset of the baseline for the current line.
-  int 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 yoffset;
-
-  // The current mode of the rendering process (box computing or rendering).
-  int mode;
-
-  // The base color of the rendered text.
-  uint8NDArray color;
-
-  // A list of parsed strings to be used for printing.
-  std::list<ft_string> strlist;
-
-  // The X offset of the baseline for the current line.
-  int line_xoffset;
-
-};
-
-#endif
-
-#endif