[HarfBuzz] harfbuzz-ng: Branch 'master' - 5 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Mon Sep 19 13:42:09 PDT 2011


 TODO                 |    2 
 configure.ac         |    6 
 src/hb-uniscribe.cc  |   30 ++--
 util/Makefile.am     |   44 +++--
 util/common.cc       |   43 -----
 util/common.hh       |   57 -------
 util/hb-shape.cc     |   78 ++++++++++
 util/hb-view.cc      |   44 -----
 util/hb-view.hh      |   79 ++++++++++
 util/helper-cairo.cc |  375 ++++++++++++++++++++++++++++++++++++++++++++++++++
 util/helper-cairo.hh |   79 ++++++++++
 util/options.cc      |   81 ++++++++++
 util/options.hh      |   60 +++++++-
 util/view-cairo.cc   |  379 ++-------------------------------------------------
 util/view-cairo.hh   |    8 -
 15 files changed, 814 insertions(+), 551 deletions(-)

New commits:
commit 8b8b19056decaf09e4e0ccd9412ee1aeb30f4de7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Sep 19 16:41:17 2011 -0400

    [util] Add hb-shape utility
    
    Like hb-view, but prints out buffer contents.
    
    The output format is kinda cryptic.  Suggestions welcome.

diff --git a/configure.ac b/configure.ac
index ac41742..2c5e728 100644
--- a/configure.ac
+++ b/configure.ac
@@ -103,12 +103,6 @@ if $have_cairo; then
 fi
 AM_CONDITIONAL(HAVE_CAIRO, $have_cairo)
 
-PKG_CHECK_MODULES(CAIRO_PNG, cairo-png, have_cairo_png=true, have_cairo_png=false)
-if $have_cairo_png; then
-	AC_DEFINE(HAVE_CAIRO_PNG, 1, [Have cairo-png support in cairo graphics library])
-fi
-AM_CONDITIONAL(HAVE_CAIRO_PNG, $have_cairo_png)
-
 PKG_CHECK_MODULES(CAIRO_FT, cairo-ft, have_cairo_ft=true, have_cairo_ft=false)
 if $have_cairo_ft; then
 	AC_DEFINE(HAVE_CAIRO_FT, 1, [Have cairo-ft support in cairo graphics library])
diff --git a/util/Makefile.am b/util/Makefile.am
index a899c26..981cb34 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -8,39 +8,51 @@ MAINTAINERCLEANFILES =
 
 bin_PROGRAMS =
 
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/ \
+	-I$(top_builddir)/src/ \
+	$(GLIB_CFLAGS) \
+	$(FREETYPE_CFLAGS) \
+	$(CAIRO_FT_CFLAGS) \
+	$(NULL)
+LDADD = \
+	$(top_builddir)/src/libharfbuzz.la \
+	-lm \
+	$(GLIB_LIBS) \
+	$(FREETYPE_LIBS) \
+	$(NULL)
+
 if HAVE_GLIB
 if HAVE_FREETYPE
 if HAVE_CAIRO_FT
-if HAVE_CAIRO_PNG
 hb_view_SOURCES = \
 	hb-view.cc \
-	common.cc \
-	common.hh \
 	options.cc \
 	options.hh \
+	helper-cairo.cc \
+	helper-cairo.hh \
 	view-cairo.cc \
 	view-cairo.hh \
 	$(NULL)
-hb_view_CPPFLAGS = \
-	-I$(top_srcdir)/src/ \
-	-I$(top_builddir)/src/ \
-	$(GLIB_CFLAGS) \
-	$(FREETYPE_CFLAGS) \
-	$(CAIRO_FT_CFLAGS) \
-	$(CAIRO_PNG_CFLAGS) \
-	$(NULL)
 hb_view_LDADD = \
-	$(top_builddir)/src/libharfbuzz.la \
-	-lm \
-	$(GLIB_LIBS) \
-	$(FREETYPE_LIBS) \
+	$(LDADD) \
+	$(CAIRO_LIBS) \
 	$(CAIRO_FT_LIBS) \
-	$(CAIRO_PNG_LIBS) \
 	$(NULL)
 bin_PROGRAMS += hb-view
 endif
 endif
 endif
+
+if HAVE_GLIB
+if HAVE_FREETYPE
+hb_shape_SOURCES = \
+	hb-shape.cc \
+	options.cc \
+	options.hh \
+	$(NULL)
+bin_PROGRAMS += hb-shape
+endif
 endif
 
 -include $(top_srcdir)/git.mk
diff --git a/util/common.cc b/util/common.cc
deleted file mode 100644
index b319914..0000000
--- a/util/common.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright © 2011  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "common.hh"
-
-void
-fail (hb_bool_t suggest_help, const char *format, ...)
-{
-  const char *msg;
-
-  va_list vap;
-  va_start (vap, format);
-  msg = g_strdup_vprintf (format, vap);
-  const char *prgname = g_get_prgname ();
-  g_printerr ("%s: %s\n", prgname, msg);
-  if (suggest_help)
-    g_printerr ("Try `%s --help' for more information.\n", prgname);
-
-  exit (1);
-}
diff --git a/util/common.hh b/util/common.hh
deleted file mode 100644
index a9a16f4..0000000
--- a/util/common.hh
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright © 2011  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-
-#ifndef COMMON_HH
-#define COMMON_HH
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <assert.h>
-#include <stdio.h>
-#include <math.h>
-#include <locale.h>
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_IO_H
-#include <io.h> /* for _setmode() under Windows */
-#endif
-
-
-#include <hb.h>
-#include <glib.h>
-#include <glib/gprintf.h>
-
-
-void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN;
-
-
-#endif
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
new file mode 100644
index 0000000..906bac2
--- /dev/null
+++ b/util/hb-shape.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2010  Behdad Esfahbod
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-view.hh"
+
+struct output_buffer_t : output_options_t, format_options_t
+{
+  output_buffer_t (option_parser_t *parser)
+		  : output_options_t (parser),
+		    format_options_t (parser) {}
+
+  void init (const font_options_t *font_opts);
+  void consume_line (hb_buffer_t  *buffer,
+		     const char   *text,
+		     unsigned int  text_len);
+  void finish (const font_options_t *font_opts);
+
+  protected:
+  GString *gs;
+  hb_font_t *font;
+};
+
+void
+output_buffer_t::init (const font_options_t *font_opts)
+{
+  get_file_handle ();
+  font = hb_font_reference (font_opts->get_font ());
+  gs = g_string_new (NULL);
+}
+
+void
+output_buffer_t::consume_line (hb_buffer_t  *buffer,
+			       const char   *text,
+			       unsigned int  text_len)
+{
+  g_string_set_size (gs, 0);
+  serialize (buffer, font, gs);
+  fprintf (fp, "%s\n", gs->str);
+}
+
+void
+output_buffer_t::finish (const font_options_t *font_opts)
+{
+  g_string_free (gs, TRUE);
+  gs = NULL;
+  hb_font_destroy (font);
+  font = NULL;
+}
+
+int
+main (int argc, char **argv)
+{
+  return hb_view_t<output_buffer_t>::main (argc, argv);
+}
diff --git a/util/hb-view.cc b/util/hb-view.cc
index d745c21..3bd1364 100644
--- a/util/hb-view.cc
+++ b/util/hb-view.cc
@@ -25,51 +25,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "common.hh"
-#include "options.hh"
-
+#include "hb-view.hh"
 #include "view-cairo.hh"
 
 int
 main (int argc, char **argv)
 {
-  setlocale (LC_ALL, "");
-
-  option_parser_t options ("[FONT-FILE] [TEXT]");
-
-  shape_options_t shaper (&options);
-  font_options_t font_opts (&options);
-  text_options_t input (&options);
-
-  view_cairo_t output (&options);
-
-  options.parse (&argc, &argv);
-
-  argc--, argv++;
-  if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++;
-  if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++;
-  if (argc)
-    fail (TRUE, "Too many arguments on the command line");
-  if (!font_opts.font_file || (!input.text && !input.text_file))
-    options.usage ();
-
-  output.init (&font_opts);
-
-  hb_buffer_t *buffer = hb_buffer_create ();
-  unsigned int text_len;
-  const char *text;
-  while ((text = input.get_line (&text_len)))
-  {
-    if (!shaper.shape (text, text_len,
-			   font_opts.get_font (),
-			   buffer))
-      fail (FALSE, "All shapers failed");
-
-    output.consume_line (buffer, text, text_len);
-  }
-  hb_buffer_destroy (buffer);
-
-  output.finish (&font_opts);
-
-  return 0;
+  return hb_view_t<view_cairo_t>::main (argc, argv);
 }
diff --git a/util/hb-view.hh b/util/hb-view.hh
new file mode 100644
index 0000000..1799cca
--- /dev/null
+++ b/util/hb-view.hh
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "common.hh"
+#include "options.hh"
+
+#ifndef HB_VIEW_HH
+#define HB_VIEW_HH
+
+template <typename output_t>
+struct hb_view_t
+{
+  static int
+  main (int argc, char **argv)
+  {
+    option_parser_t options ("[FONT-FILE] [TEXT]");
+
+    shape_options_t shaper (&options);
+    font_options_t font_opts (&options);
+    text_options_t input (&options);
+
+    output_t output (&options);
+
+    options.parse (&argc, &argv);
+
+    argc--, argv++;
+    if (argc && !font_opts.font_file) font_opts.font_file = argv[0], argc--, argv++;
+    if (argc && !input.text && !input.text_file) input.text = argv[0], argc--, argv++;
+    if (argc)
+      fail (TRUE, "Too many arguments on the command line");
+    if (!font_opts.font_file || (!input.text && !input.text_file))
+      options.usage ();
+
+    output.init (&font_opts);
+
+    hb_buffer_t *buffer = hb_buffer_create ();
+    unsigned int text_len;
+    const char *text;
+    while ((text = input.get_line (&text_len)))
+    {
+      if (!shaper.shape (text, text_len,
+			 font_opts.get_font (),
+			 buffer))
+	fail (FALSE, "All shapers failed");
+
+      output.consume_line (buffer, text, text_len);
+    }
+    hb_buffer_destroy (buffer);
+
+    output.finish (&font_opts);
+
+    return 0;
+  }
+};
+
+#endif
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
new file mode 100644
index 0000000..3a6d913
--- /dev/null
+++ b/util/helper-cairo.cc
@@ -0,0 +1,375 @@
+/*
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "helper-cairo.hh"
+
+#include <cairo-ft.h>
+#include <hb-ft.h>
+
+#ifdef CAIRO_HAS_SVG_SURFACE
+#  include <cairo-svg.h>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+#  include <cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#  include <cairo-ps.h>
+#  if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
+#    define HAS_EPS 1
+
+static cairo_surface_t *
+_cairo_eps_surface_create_for_stream (cairo_write_func_t  write_func,
+				      void               *closure,
+				      double              width,
+				      double              height)
+{
+  cairo_surface_t *surface;
+
+  surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
+  cairo_ps_surface_set_eps (surface, TRUE);
+
+  return surface;
+}
+
+#  else
+#    undef HAS_EPS
+#  endif
+#endif
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+				 double font_size)
+{
+  hb_font_t *font = hb_font_reference (font_opts->get_font ());
+
+  cairo_font_face_t *cairo_face;
+  FT_Face ft_face = hb_ft_font_get_face (font);
+  if (!ft_face)
+    /* This allows us to get some boxes at least... */
+    cairo_face = cairo_toy_font_face_create ("@cairo:sans",
+					     CAIRO_FONT_SLANT_NORMAL,
+					     CAIRO_FONT_WEIGHT_NORMAL);
+  else
+    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+  cairo_matrix_t ctm, font_matrix;
+  cairo_font_options_t *font_options;
+
+  cairo_matrix_init_identity (&ctm);
+  cairo_matrix_init_scale (&font_matrix,
+			   font_size, font_size);
+  font_options = cairo_font_options_create ();
+  cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+  cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
+
+  cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
+							       &font_matrix,
+							       &ctm,
+							       font_options);
+
+  cairo_font_options_destroy (font_options);
+  cairo_font_face_destroy (cairo_face);
+
+  static cairo_user_data_key_t key;
+  if (cairo_scaled_font_set_user_data (scaled_font,
+				       &key,
+				       (void *) font,
+				       (cairo_destroy_func_t) hb_font_destroy))
+    hb_font_destroy (font);
+
+  return scaled_font;
+}
+
+
+struct finalize_closure_t {
+  void (*callback)(finalize_closure_t *);
+  cairo_surface_t *surface;
+  cairo_write_func_t write_func;
+  void *closure;
+};
+static cairo_user_data_key_t finalize_closure_key;
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+
+static void
+finalize_png (finalize_closure_t *closure)
+{
+  cairo_status_t status;
+  status = cairo_surface_write_to_png_stream (closure->surface,
+					      closure->write_func,
+					      closure->closure);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (FALSE, "Failed to write output: %s",
+	  cairo_status_to_string (status));
+}
+
+static cairo_surface_t *
+_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
+				      void *closure,
+				      double width,
+				      double height,
+				      cairo_content_t content)
+{
+  cairo_surface_t *surface;
+  int w = ceil (width);
+  int h = ceil (height);
+
+  switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
+      break;
+    default:
+    case CAIRO_CONTENT_COLOR:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+      break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+      break;
+  }
+  cairo_status_t status = cairo_surface_status (surface);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (FALSE, "Failed to create cairo surface: %s",
+	  cairo_status_to_string (status));
+
+  finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
+  png_closure->callback = finalize_png;
+  png_closure->surface = surface;
+  png_closure->write_func = write_func;
+  png_closure->closure = closure;
+
+  if (cairo_surface_set_user_data (surface,
+				   &finalize_closure_key,
+				   (void *) png_closure,
+				   (cairo_destroy_func_t) g_free))
+    g_free ((void *) closure);
+
+  return surface;
+}
+
+#endif
+
+static cairo_status_t
+stdio_write_func (void                *closure,
+		  const unsigned char *data,
+		  unsigned int         size)
+{
+  FILE *fp = (FILE *) closure;
+
+  while (size) {
+    size_t ret = fwrite (data, 1, size, fp);
+    size -= ret;
+    data += ret;
+    if (size && ferror (fp))
+      fail (FALSE, "Failed to write output: %s", strerror (errno));
+  }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+			     view_options_t *view_opts,
+			     output_options_t *out_opts)
+{
+  cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
+				   void *closure,
+				   double width,
+				   double height) = NULL;
+  cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
+				    void *closure,
+				    double width,
+				    double height,
+				    cairo_content_t content) = NULL;
+
+  const char *extension = out_opts->output_format;
+  if (!extension)
+    extension = "png";
+  if (0)
+    ;
+  #ifdef CAIRO_HAS_PNG_FUNCTIONS
+    else if (0 == strcasecmp (extension, "png"))
+      constructor2 = _cairo_png_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_SVG_SURFACE
+    else if (0 == strcasecmp (extension, "svg"))
+      constructor = cairo_svg_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_PDF_SURFACE
+    else if (0 == strcasecmp (extension, "pdf"))
+      constructor = cairo_pdf_surface_create_for_stream;
+  #endif
+  #ifdef CAIRO_HAS_PS_SURFACE
+    else if (0 == strcasecmp (extension, "ps"))
+      constructor = cairo_ps_surface_create_for_stream;
+   #ifdef HAS_EPS
+    else if (0 == strcasecmp (extension, "eps"))
+      constructor = _cairo_eps_surface_create_for_stream;
+   #endif
+  #endif
+
+
+  unsigned int fr, fg, fb, fa, br, bg, bb, ba;
+  br = bg = bb = ba = 255;
+  sscanf (view_opts->back + (*view_opts->back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
+  fr = fg = fb = 0; fa = 255;
+  sscanf (view_opts->fore + (*view_opts->fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
+
+  cairo_content_t content;
+  if (!view_opts->annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
+    content = CAIRO_CONTENT_ALPHA;
+  else if (ba == 255)
+    content = CAIRO_CONTENT_COLOR;
+  else
+    content = CAIRO_CONTENT_COLOR_ALPHA;
+
+  cairo_surface_t *surface;
+  FILE *f = out_opts->get_file_handle ();
+  if (constructor)
+    surface = constructor (stdio_write_func, f, w, h);
+  else if (constructor2)
+    surface = constructor2 (stdio_write_func, f, w, h, content);
+  else
+    fail (FALSE, "Unknown output format `%s'", extension);
+
+  cairo_t *cr = cairo_create (surface);
+  content = cairo_surface_get_content (surface);
+
+  switch (content) {
+    case CAIRO_CONTENT_ALPHA:
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
+      cairo_paint (cr);
+      cairo_set_source_rgba (cr, 1., 1., 1.,
+			     (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
+      break;
+    default:
+    case CAIRO_CONTENT_COLOR:
+    case CAIRO_CONTENT_COLOR_ALPHA:
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
+      cairo_paint (cr);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
+      break;
+  }
+
+  cairo_surface_destroy (surface);
+  return cr;
+}
+
+void
+helper_cairo_destroy_context (cairo_t *cr)
+{
+  finalize_closure_t *closure = (finalize_closure_t *)
+				cairo_surface_get_user_data (cairo_get_target (cr),
+							     &finalize_closure_key);
+  if (closure)
+    closure->callback (closure);
+
+  cairo_status_t status = cairo_status (cr);
+  if (status != CAIRO_STATUS_SUCCESS)
+    fail (FALSE, "Failed: %s",
+	  cairo_status_to_string (status));
+  cairo_destroy (cr);
+}
+
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+			       hb_buffer_t         *buffer,
+			       const char          *text,
+			       unsigned int         text_len,
+			       double               scale)
+{
+  memset (l, 0, sizeof (*l));
+
+  l->num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
+  l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
+
+  if (text) {
+    l->utf8 = g_strndup (text, text_len);
+    l->utf8_len = text_len;
+    l->num_clusters = l->num_glyphs ? 1 : 0;
+    for (unsigned int i = 1; i < l->num_glyphs; i++)
+      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+	l->num_clusters++;
+    l->clusters = cairo_text_cluster_allocate (l->num_clusters);
+  }
+
+  if ((l->num_glyphs && !l->glyphs) ||
+      (l->utf8_len && !l->utf8) ||
+      (l->num_clusters && !l->clusters))
+  {
+    l->finish ();
+    return;
+  }
+
+  hb_position_t x = 0, y = 0;
+  int i;
+  for (i = 0; i < (int) l->num_glyphs; i++)
+  {
+    l->glyphs[i].index = hb_glyph[i].codepoint;
+    l->glyphs[i].x = ( hb_position->x_offset + x) * scale;
+    l->glyphs[i].y = (-hb_position->y_offset + y) * scale;
+    x +=  hb_position->x_advance;
+    y += -hb_position->y_advance;
+
+    hb_position++;
+  }
+  l->glyphs[i].index = -1;
+  l->glyphs[i].x = x * scale;
+  l->glyphs[i].y = y * scale;
+
+  if (l->num_clusters) {
+    memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0]));
+    bool backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
+    l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
+    unsigned int cluster = 0;
+    l->clusters[cluster].num_glyphs++;
+    if (backward) {
+      for (i = l->num_glyphs - 2; i >= 0; i--) {
+	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
+	  g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
+	  l->clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
+	  cluster++;
+	}
+	l->clusters[cluster].num_glyphs++;
+      }
+      l->clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
+    } else {
+      for (i = 1; i < (int) l->num_glyphs; i++) {
+	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
+	  g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
+	  l->clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
+	  cluster++;
+	}
+	l->clusters[cluster].num_glyphs++;
+      }
+      l->clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
+    }
+  }
+}
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
new file mode 100644
index 0000000..5edfc3a
--- /dev/null
+++ b/util/helper-cairo.hh
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "options.hh"
+
+#include <cairo.h>
+
+#ifndef HELPER_CAIRO_HH
+#define HELPER_CAIRO_HH
+
+
+cairo_scaled_font_t *
+helper_cairo_create_scaled_font (const font_options_t *font_opts,
+				 double font_size);
+
+
+cairo_t *
+helper_cairo_create_context (double w, double h,
+			     view_options_t *view_opts,
+			     output_options_t *out_opts);
+
+void
+helper_cairo_destroy_context (cairo_t *cr);
+
+
+struct helper_cairo_line_t {
+  cairo_glyph_t *glyphs;
+  unsigned int num_glyphs;
+  char *utf8;
+  unsigned int utf8_len;
+  cairo_text_cluster_t *clusters;
+  unsigned int num_clusters;
+  cairo_text_cluster_flags_t cluster_flags;
+
+  void finish (void) {
+    if (glyphs)
+      cairo_glyph_free (glyphs);
+    if (clusters)
+      cairo_text_cluster_free (clusters);
+    if (utf8)
+      g_free (utf8);
+  }
+
+  double get_width (void) {
+    return glyphs[num_glyphs].x;
+  }
+};
+
+void
+helper_cairo_line_from_buffer (helper_cairo_line_t *l,
+			       hb_buffer_t         *buffer,
+			       const char          *text,
+			       unsigned int         text_len,
+			       double               scale);
+
+#endif
diff --git a/util/options.cc b/util/options.cc
index 96d131f..21ab973 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -31,6 +31,23 @@
 #endif
 
 
+void
+fail (hb_bool_t suggest_help, const char *format, ...)
+{
+  const char *msg;
+
+  va_list vap;
+  va_start (vap, format);
+  msg = g_strdup_vprintf (format, vap);
+  const char *prgname = g_get_prgname ();
+  g_printerr ("%s: %s\n", prgname, msg);
+  if (suggest_help)
+    g_printerr ("Try `%s --help' for more information.\n", prgname);
+
+  exit (1);
+}
+
+
 bool debug = FALSE;
 
 static gchar *
@@ -118,6 +135,8 @@ option_parser_t::add_group (GOptionEntry   *entries,
 void
 option_parser_t::parse (int *argc, char ***argv)
 {
+  setlocale (LC_ALL, "");
+
   GError *parse_error = NULL;
   if (!g_option_context_parse (context, argc, argv, &parse_error))
   {
@@ -590,3 +609,63 @@ output_options_t::get_file_handle (void)
 
   return fp;
 }
+
+
+void
+format_options_t::add_options (option_parser_t *parser)
+{
+  GOptionEntry entries[] =
+  {
+    {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Use glyph indices instead of names",	NULL},
+    {"no-positions",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_positions,		"Do not show glyph positions",		NULL},
+    {"no-clusters",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not show cluster mapping",		NULL},
+    {NULL}
+  };
+  parser->add_group (entries,
+		     "format",
+		     "Format options:",
+		     "Options controlling the formatting of buffer contents",
+		     this);
+}
+
+void
+format_options_t::serialize (hb_buffer_t *buffer,
+			     hb_font_t   *font,
+			     GString     *gs)
+{
+  FT_Face ft_face = show_glyph_names ? hb_ft_font_get_face (font) : NULL;
+
+  unsigned int num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+  g_string_append_c (gs, '<');
+  for (unsigned int i = 0; i < (int) num_glyphs; i++)
+  {
+    if (i)
+      g_string_append_c (gs, '|');
+    char glyph_name[30];
+    if (show_glyph_names && !FT_Get_Glyph_Name (ft_face, info->codepoint, glyph_name, sizeof (glyph_name)))
+      g_string_append_printf (gs, "%s", glyph_name);
+    else
+      g_string_append_printf (gs, "gid%d", info->codepoint);
+
+    if (show_clusters)
+      g_string_append_printf (gs, "=%d", info->cluster);
+
+    if (show_positions && (pos->x_offset || pos->y_offset)) {
+      g_string_append_c (gs, '@');
+      if (pos->x_offset) g_string_append_printf (gs, "%d", pos->x_offset);
+      if (pos->y_offset) g_string_append_printf (gs, ",%d", pos->y_offset);
+    }
+    if (show_positions && (pos->x_advance || pos->y_advance)) {
+      g_string_append_c (gs, '+');
+      if (pos->x_advance) g_string_append_printf (gs, "%d", pos->x_advance);
+      if (pos->y_advance) g_string_append_printf (gs, ",%d", pos->y_advance);
+    }
+
+    info++;
+    pos++;
+  }
+  g_string_append_c (gs, '>');
+}
diff --git a/util/options.hh b/util/options.hh
index e8747a6..af9bedb 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -24,12 +24,34 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "common.hh"
-
 #ifndef OPTIONS_HH
 #define OPTIONS_HH
 
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_IO_H
+#include <io.h> /* for _setmode() under Windows */
+#endif
+
+#include <hb.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN;
+
+
 extern bool debug;
 
 struct option_group_t
@@ -248,12 +270,37 @@ struct output_options_t : option_group_t
 			     unsigned int  text_len) = 0;
   virtual void finish (const font_options_t *font_opts) = 0;
 
-  protected:
   const char *output_file;
   const char *output_format;
 
+  protected:
+
   mutable FILE *fp;
 };
 
+struct format_options_t : option_group_t
+{
+  format_options_t (option_parser_t *parser) {
+    show_glyph_names = true;
+    show_positions = true;
+    show_clusters = true;
+
+    add_options (parser);
+  }
+  ~format_options_t (void) {
+  }
+
+  void add_options (option_parser_t *parser);
+
+  void serialize (hb_buffer_t *buffer,
+		  hb_font_t   *font,
+		  GString     *gs);
+
+  protected:
+  bool show_glyph_names;
+  bool show_positions;
+  bool show_clusters;
+};
+
 
 #endif
diff --git a/util/view-cairo.cc b/util/view-cairo.cc
index e3c49a4..4671147 100644
--- a/util/view-cairo.cc
+++ b/util/view-cairo.cc
@@ -26,60 +26,10 @@
 
 #include "view-cairo.hh"
 
-
-#ifdef CAIRO_HAS_SVG_SURFACE
-#  include <cairo-svg.h>
-#endif
-#ifdef CAIRO_HAS_PDF_SURFACE
-#  include <cairo-pdf.h>
-#endif
-#ifdef CAIRO_HAS_PS_SURFACE
-#  include <cairo-ps.h>
-#  if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
-#    define HAS_EPS 1
-
-static cairo_surface_t *
-_cairo_eps_surface_create_for_stream (cairo_write_func_t  write_func,
-				      void               *closure,
-				      double              width,
-				      double              height)
-{
-  cairo_surface_t *surface;
-
-  surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height);
-  cairo_ps_surface_set_eps (surface, TRUE);
-
-  return surface;
-}
-
-#  else
-#    undef HAS_EPS
-#  endif
-#endif
-
-struct line_t {
-  cairo_glyph_t *glyphs;
-  unsigned int num_glyphs;
-  char *utf8;
-  unsigned int utf8_len;
-  cairo_text_cluster_t *clusters;
-  unsigned int num_clusters;
-  cairo_text_cluster_flags_t cluster_flags;
-
-  void finish (void) {
-    if (glyphs)
-      cairo_glyph_free (glyphs);
-    if (clusters)
-      cairo_text_cluster_free (clusters);
-    if (utf8)
-      g_free (utf8);
-  }
-};
-
 void
 view_cairo_t::init (const font_options_t *font_opts)
 {
-  lines = g_array_new (FALSE, FALSE, sizeof (line_t));
+  lines = g_array_new (FALSE, FALSE, sizeof (helper_cairo_line_t));
   scale = double (font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
 }
 
@@ -88,82 +38,8 @@ view_cairo_t::consume_line (hb_buffer_t  *buffer,
 			    const char   *text,
 			    unsigned int  text_len)
 {
-  line_t l = {0};
-
-  l.num_glyphs = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
-  l.glyphs = cairo_glyph_allocate (l.num_glyphs + 1);
-
-  if (text) {
-    l.utf8 = g_strndup (text, text_len);
-    l.utf8_len = text_len;
-    l.num_clusters = l.num_glyphs ? 1 : 0;
-    for (unsigned int i = 1; i < l.num_glyphs; i++)
-      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
-	l.num_clusters++;
-    l.clusters = cairo_text_cluster_allocate (l.num_clusters);
-  } else {
-    l.utf8_len = 0;
-    l.num_clusters = 0;
-  }
-
-  if ((l.num_glyphs && !l.glyphs) ||
-      (l.utf8_len && !l.utf8) ||
-      (l.num_clusters && !l.clusters))
-  {
-    l.finish ();
-    return;
-  }
-
-  hb_position_t x = 0, y = 0;
-  int i;
-  for (i = 0; i < (int) l.num_glyphs; i++)
-  {
-    l.glyphs[i].index = hb_glyph[i].codepoint;
-    l.glyphs[i].x = ( hb_position->x_offset + x) * scale;
-    l.glyphs[i].y = (-hb_position->y_offset + y) * scale;
-    x +=  hb_position->x_advance;
-    y += -hb_position->y_advance;
-
-    hb_position++;
-  }
-  l.glyphs[i].index = 0;
-  l.glyphs[i].x = x;
-  l.glyphs[i].y = y;
-
-  if (l.num_clusters) {
-    memset ((void *) l.clusters, 0, l.num_clusters * sizeof (l.clusters[0]));
-    bool backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
-    l.cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
-    unsigned int cluster = 0;
-    if (!l.num_glyphs)
-      goto done;
-    l.clusters[cluster].num_glyphs++;
-    if (backward) {
-      for (i = l.num_glyphs - 2; i >= 0; i--) {
-	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
-	  g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
-	  l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
-	  cluster++;
-	}
-	l.clusters[cluster].num_glyphs++;
-      }
-      l.clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
-    } else {
-      for (i = 1; i < (int) l.num_glyphs; i++) {
-	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
-	  g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
-	  l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
-	  cluster++;
-	}
-	l.clusters[cluster].num_glyphs++;
-      }
-      l.clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
-    }
-  }
-
-done:
+  helper_cairo_line_t l;
+  helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale);
   g_array_append_val (lines, l);
 }
 
@@ -173,20 +49,12 @@ view_cairo_t::finish (const font_options_t *font_opts)
   render (font_opts);
 
   for (unsigned int i = 0; i < lines->len; i++) {
-    line_t &line = g_array_index (lines, line_t, i);
+    helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
     line.finish ();
   }
-
   g_array_unref (lines);
 }
 
-double
-view_cairo_t::line_width (unsigned int i)
-{
-  line_t &line = g_array_index (lines, line_t, i);
-  return line.glyphs[line.num_glyphs].x * scale;
-}
-
 void
 view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font,
 				double *w, double *h)
@@ -199,252 +67,29 @@ view_cairo_t::get_surface_size (cairo_scaled_font_t *scaled_font,
      + font_extents.descent
      + ((int) lines->len - 1) * font_extents.height;
   *w = 0;
-  for (unsigned int i = 0; i < lines->len; i++)
-    *w = MAX (*w, line_width (i));
+  for (unsigned int i = 0; i < lines->len; i++) {
+    helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i);
+    double line_width = line.get_width ();
+    *w = MAX (*w, line_width);
+  }
 
   *w += margin.l + margin.r;
   *h += margin.t + margin.b;
 }
 
-cairo_scaled_font_t *
-view_cairo_t::create_scaled_font (const font_options_t *font_opts)
-{
-  hb_font_t *font = hb_font_reference (font_opts->get_font ());
-
-  cairo_font_face_t *cairo_face;
-  FT_Face ft_face = hb_ft_font_get_face (font);
-  if (!ft_face)
-    /* This allows us to get some boxes at least... */
-    cairo_face = cairo_toy_font_face_create ("@cairo:sans",
-					     CAIRO_FONT_SLANT_NORMAL,
-					     CAIRO_FONT_WEIGHT_NORMAL);
-  else
-    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
-  cairo_matrix_t ctm, font_matrix;
-  cairo_font_options_t *font_options;
-
-  cairo_matrix_init_identity (&ctm);
-  cairo_matrix_init_scale (&font_matrix,
-			   font_size, font_size);
-  font_options = cairo_font_options_create ();
-  cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
-  cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
-
-  cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face,
-							       &font_matrix,
-							       &ctm,
-							       font_options);
-
-  cairo_font_options_destroy (font_options);
-  cairo_font_face_destroy (cairo_face);
-
-  static cairo_user_data_key_t key;
-  if (cairo_scaled_font_set_user_data (scaled_font,
-				       &key,
-				       (void *) font,
-				       (cairo_destroy_func_t) hb_font_destroy))
-    hb_font_destroy (font);
-
-  return scaled_font;
-}
-
-struct finalize_closure_t {
-  void (*callback)(finalize_closure_t *);
-  cairo_surface_t *surface;
-  cairo_write_func_t write_func;
-  void *closure;
-};
-static cairo_user_data_key_t finalize_closure_key;
-
-#ifdef CAIRO_HAS_PNG_FUNCTIONS
-
-static void
-finalize_png (finalize_closure_t *closure)
-{
-  cairo_status_t status;
-  status = cairo_surface_write_to_png_stream (closure->surface,
-					      closure->write_func,
-					      closure->closure);
-  if (status != CAIRO_STATUS_SUCCESS)
-    fail (FALSE, "Failed to write output: %s",
-	  cairo_status_to_string (status));
-}
-
-static cairo_surface_t *
-_cairo_png_surface_create_for_stream (cairo_write_func_t write_func,
-				      void *closure,
-				      double width,
-				      double height,
-				      cairo_content_t content)
-{
-  cairo_surface_t *surface;
-  int w = ceil (width);
-  int h = ceil (height);
-
-  switch (content) {
-    case CAIRO_CONTENT_ALPHA:
-      surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h);
-      break;
-    default:
-    case CAIRO_CONTENT_COLOR:
-      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
-      break;
-    case CAIRO_CONTENT_COLOR_ALPHA:
-      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
-      break;
-  }
-  cairo_status_t status = cairo_surface_status (surface);
-  if (status != CAIRO_STATUS_SUCCESS)
-    fail (FALSE, "Failed to create cairo surface: %s",
-	  cairo_status_to_string (status));
-
-  finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1);
-  png_closure->callback = finalize_png;
-  png_closure->surface = surface;
-  png_closure->write_func = write_func;
-  png_closure->closure = closure;
-
-  if (cairo_surface_set_user_data (surface,
-				   &finalize_closure_key,
-				   (void *) png_closure,
-				   (cairo_destroy_func_t) g_free))
-    g_free ((void *) closure);
-
-  return surface;
-}
-
-#endif
-
 void
 view_cairo_t::render (const font_options_t *font_opts)
 {
-  cairo_scaled_font_t *scaled_font = create_scaled_font (font_opts);
+  cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts, font_size);
   double w, h;
   get_surface_size (scaled_font, &w, &h);
-  cairo_t *cr = create_context (w, h);
+  cairo_t *cr = helper_cairo_create_context (w, h, this, this);
   cairo_set_scaled_font (cr, scaled_font);
   cairo_scaled_font_destroy (scaled_font);
 
   draw (cr);
 
-  finalize_closure_t *closure = (finalize_closure_t *)
-				cairo_surface_get_user_data (cairo_get_target (cr),
-							     &finalize_closure_key);
-  if (closure)
-    closure->callback (closure);
-
-  cairo_status_t status = cairo_status (cr);
-  if (status != CAIRO_STATUS_SUCCESS)
-    fail (FALSE, "Failed: %s",
-	  cairo_status_to_string (status));
-  cairo_destroy (cr);
-}
-
-static cairo_status_t
-stdio_write_func (void                *closure,
-		  const unsigned char *data,
-		  unsigned int         size)
-{
-  FILE *fp = (FILE *) closure;
-
-  while (size) {
-    size_t ret = fwrite (data, 1, size, fp);
-    size -= ret;
-    data += ret;
-    if (size && ferror (fp))
-      fail (FALSE, "Failed to write output: %s", strerror (errno));
-  }
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_t *
-view_cairo_t::create_context (double w, double h)
-{
-  cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
-				   void *closure,
-				   double width,
-				   double height) = NULL;
-  cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
-				    void *closure,
-				    double width,
-				    double height,
-				    cairo_content_t content) = NULL;
-
-  const char *extension = output_format;
-  if (!extension)
-    extension = "png";
-  if (0)
-    ;
-  #ifdef CAIRO_HAS_PNG_FUNCTIONS
-    else if (0 == strcasecmp (extension, "png"))
-      constructor2 = _cairo_png_surface_create_for_stream;
-  #endif
-  #ifdef CAIRO_HAS_SVG_SURFACE
-    else if (0 == strcasecmp (extension, "svg"))
-      constructor = cairo_svg_surface_create_for_stream;
-  #endif
-  #ifdef CAIRO_HAS_PDF_SURFACE
-    else if (0 == strcasecmp (extension, "pdf"))
-      constructor = cairo_pdf_surface_create_for_stream;
-  #endif
-  #ifdef CAIRO_HAS_PS_SURFACE
-    else if (0 == strcasecmp (extension, "ps"))
-      constructor = cairo_ps_surface_create_for_stream;
-   #ifdef HAS_EPS
-    else if (0 == strcasecmp (extension, "eps"))
-      constructor = _cairo_eps_surface_create_for_stream;
-   #endif
-  #endif
-
-
-  unsigned int fr, fg, fb, fa, br, bg, bb, ba;
-  br = bg = bb = ba = 255;
-  sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
-  fr = fg = fb = 0; fa = 255;
-  sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
-
-  cairo_content_t content;
-  if (!annotate && ba == 255 && br == bg && bg == bb && fr == fg && fg == fb)
-    content = CAIRO_CONTENT_ALPHA;
-  else if (ba == 255)
-    content = CAIRO_CONTENT_COLOR;
-  else
-    content = CAIRO_CONTENT_COLOR_ALPHA;
-
-  cairo_surface_t *surface;
-  FILE *f = get_file_handle ();
-  if (constructor)
-    surface = constructor (stdio_write_func, f, w, h);
-  else if (constructor2)
-    surface = constructor2 (stdio_write_func, f, w, h, content);
-  else
-    fail (FALSE, "Unknown output format `%s'", extension);
-
-  cairo_t *cr = cairo_create (surface);
-  content = cairo_surface_get_content (surface);
-
-  switch (content) {
-    case CAIRO_CONTENT_ALPHA:
-      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-      cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
-      cairo_paint (cr);
-      cairo_set_source_rgba (cr, 1., 1., 1.,
-			     (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.)));
-      break;
-    default:
-    case CAIRO_CONTENT_COLOR:
-    case CAIRO_CONTENT_COLOR_ALPHA:
-      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-      cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
-      cairo_paint (cr);
-      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-      cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
-      break;
-  }
-
-  cairo_surface_destroy (surface);
-  return cr;
+  helper_cairo_destroy_context (cr);
 }
 
 void
@@ -457,7 +102,7 @@ view_cairo_t::draw (cairo_t *cr)
   cairo_translate (cr, margin.l, margin.t);
   for (unsigned int i = 0; i < lines->len; i++)
   {
-    line_t &l = g_array_index (lines, line_t, i);
+    helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i);
 
     if (i)
       cairo_translate (cr, 0, line_space);
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
index 863039b..564f5c7 100644
--- a/util/view-cairo.hh
+++ b/util/view-cairo.hh
@@ -25,13 +25,12 @@
  */
 
 #include "options.hh"
-
-#include <cairo-ft.h>
-#include <hb-ft.h>
+#include "helper-cairo.hh"
 
 #ifndef VIEW_CAIRO_HH
 #define VIEW_CAIRO_HH
 
+
 struct view_cairo_t : output_options_t, view_options_t {
   view_cairo_t (option_parser_t *parser)
 	       : output_options_t (parser),
@@ -50,11 +49,8 @@ struct view_cairo_t : output_options_t, view_options_t {
   protected:
 
   void render (const font_options_t *font_opts);
-  cairo_scaled_font_t *create_scaled_font (const font_options_t *font_opts);
   void get_surface_size (cairo_scaled_font_t *scaled_font, double *w, double *h);
-  cairo_t *create_context (double w, double h);
   void draw (cairo_t *cr);
-  double line_width (unsigned int i);
 
   GArray *lines;
   double scale;
commit eb2d8be7a8ede0c0f5e346cf06516792f83f36f7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Sep 19 16:15:22 2011 -0400

    Minor

diff --git a/TODO b/TODO
index d11c0a2..d064e48 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,8 @@
 General fixes:
 =============
 
+- 'const' for getter APIs? (use mutable internally)
+
 - Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS)
 
 - Do proper rounding when scaling from font space?
commit f6496663c2f6849a944e41afcf9511f378477532
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Sep 19 15:45:52 2011 -0400

    [util] If no text is provided, simply call cairo_show_glyphs()

diff --git a/util/view-cairo.cc b/util/view-cairo.cc
index c6278f1..e3c49a4 100644
--- a/util/view-cairo.cc
+++ b/util/view-cairo.cc
@@ -94,13 +94,19 @@ view_cairo_t::consume_line (hb_buffer_t  *buffer,
   hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
   hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
   l.glyphs = cairo_glyph_allocate (l.num_glyphs + 1);
-  l.utf8 = g_strndup (text, text_len);
-  l.utf8_len = text_len;
-  l.num_clusters = l.num_glyphs ? 1 : 0;
-  for (unsigned int i = 1; i < l.num_glyphs; i++)
-    if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
-      l.num_clusters++;
-  l.clusters = cairo_text_cluster_allocate (l.num_clusters);
+
+  if (text) {
+    l.utf8 = g_strndup (text, text_len);
+    l.utf8_len = text_len;
+    l.num_clusters = l.num_glyphs ? 1 : 0;
+    for (unsigned int i = 1; i < l.num_glyphs; i++)
+      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+	l.num_clusters++;
+    l.clusters = cairo_text_cluster_allocate (l.num_clusters);
+  } else {
+    l.utf8_len = 0;
+    l.num_clusters = 0;
+  }
 
   if ((l.num_glyphs && !l.glyphs) ||
       (l.utf8_len && !l.utf8) ||
@@ -113,46 +119,48 @@ view_cairo_t::consume_line (hb_buffer_t  *buffer,
   hb_position_t x = 0, y = 0;
   int i;
   for (i = 0; i < (int) l.num_glyphs; i++)
-    {
-      l.glyphs[i].index = hb_glyph[i].codepoint;
-      l.glyphs[i].x = ( hb_position->x_offset + x) * scale;
-      l.glyphs[i].y = (-hb_position->y_offset + y) * scale;
-      x +=  hb_position->x_advance;
-      y += -hb_position->y_advance;
-
-      hb_position++;
-    }
+  {
+    l.glyphs[i].index = hb_glyph[i].codepoint;
+    l.glyphs[i].x = ( hb_position->x_offset + x) * scale;
+    l.glyphs[i].y = (-hb_position->y_offset + y) * scale;
+    x +=  hb_position->x_advance;
+    y += -hb_position->y_advance;
+
+    hb_position++;
+  }
   l.glyphs[i].index = 0;
   l.glyphs[i].x = x;
   l.glyphs[i].y = y;
 
-  memset ((void *) l.clusters, 0, l.num_clusters * sizeof (l.clusters[0]));
-  bool backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
-  l.cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
-  unsigned int cluster = 0;
-  if (!l.num_glyphs)
-    goto done;
-  l.clusters[cluster].num_glyphs++;
-  if (backward) {
-    for (i = l.num_glyphs - 2; i >= 0; i--) {
-      if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
-        g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
-	l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
-        cluster++;
+  if (l.num_clusters) {
+    memset ((void *) l.clusters, 0, l.num_clusters * sizeof (l.clusters[0]));
+    bool backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
+    l.cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
+    unsigned int cluster = 0;
+    if (!l.num_glyphs)
+      goto done;
+    l.clusters[cluster].num_glyphs++;
+    if (backward) {
+      for (i = l.num_glyphs - 2; i >= 0; i--) {
+	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) {
+	  g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
+	  l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i+1].cluster;
+	  cluster++;
+	}
+	l.clusters[cluster].num_glyphs++;
       }
-      l.clusters[cluster].num_glyphs++;
-    }
-    l.clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
-  } else {
-    for (i = 1; i < (int) l.num_glyphs; i++) {
-      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
-        g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
-	l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
-        cluster++;
+      l.clusters[cluster].num_bytes += text_len - hb_glyph[0].cluster;
+    } else {
+      for (i = 1; i < (int) l.num_glyphs; i++) {
+	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) {
+	  g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
+	  l.clusters[cluster].num_bytes += hb_glyph[i].cluster - hb_glyph[i-1].cluster;
+	  cluster++;
+	}
+	l.clusters[cluster].num_glyphs++;
       }
-      l.clusters[cluster].num_glyphs++;
+      l.clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
     }
-    l.clusters[cluster].num_bytes += text_len - hb_glyph[i - 1].cluster;
   }
 
 done:
@@ -476,12 +484,14 @@ view_cairo_t::draw (cairo_t *cr)
       /* cairo_show_glyphs() doesn't support subpixel positioning */
       cairo_glyph_path (cr, l.glyphs, l.num_glyphs);
       cairo_fill (cr);
-    } else
+    } else if (l.num_clusters)
       cairo_show_text_glyphs (cr,
 			      l.utf8, l.utf8_len,
 			      l.glyphs, l.num_glyphs,
 			      l.clusters, l.num_clusters,
 			      l.cluster_flags);
+    else
+      cairo_show_glyphs (cr, l.glyphs, l.num_glyphs);
 
     cairo_translate (cr, 0, font_extents.height - font_extents.ascent);
   }
commit 5c299343118d1eaff32ffb2a5dac077cfff67dee
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Sep 19 14:53:26 2011 -0400

    [uniscribe] Various improvements

diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 9026bb0..ce86074 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -300,17 +300,22 @@ retry:
 #define MAX_ITEMS 10
 
   SCRIPT_ITEM items[MAX_ITEMS + 1];
+  SCRIPT_CONTROL bidi_control = {0};
   SCRIPT_STATE bidi_state = {0};
   WIN_ULONG script_tags[MAX_ITEMS];
   int item_count;
 
+  /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */
+  //bidi_control.fMergeNeutralItems = TRUE;
+  *(uint32_t*)&bidi_control |= 1<<24;
+
   bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
-  bidi_state.fOverrideDirection = 1;
+//  bidi_state.fOverrideDirection = 1;
 
   hr = ScriptItemizeOpenType (wchars,
 			      chars_len,
 			      MAX_ITEMS,
-			      NULL,
+			      &bidi_control,
 			      &bidi_state,
 			      items,
 			      script_tags,
@@ -324,7 +329,7 @@ retry:
   TEXTRANGE_PROPERTIES **range_properties = NULL;
   int range_count = 0;
   if (num_features) {
-    /* XXX setup ranges */
+    /* TODO setup ranges */
   }
 
   OPENTYPE_TAG language_tag = hb_ot_tag_from_language (buffer->props.language);
@@ -399,15 +404,21 @@ retry:
    * very, *very*, carefully! */
 
   /* Calculate visual-clusters.  That's what we ship. */
-  for (unsigned int i = 0; i < buffer->len; i++)
-    vis_clusters[i] = 0;
+  for (unsigned int i = 0; i < glyphs_len; i++)
+    vis_clusters[i] = -1;
   for (unsigned int i = 0; i < buffer->len; i++) {
     uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]];
     *p = MIN (*p, buffer->info[i].cluster);
   }
-  for (unsigned int i = 1; i < glyphs_len; i++)
-    if (!glyph_props[i].sva.fClusterStart)
-    vis_clusters[i] = vis_clusters[i - 1];
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+    for (unsigned int i = 1; i < glyphs_len; i++)
+      if (!glyph_props[i].sva.fClusterStart)
+	vis_clusters[i] = vis_clusters[i - 1];
+  } else {
+    for (int i = glyphs_len - 2; i >= 0; i--)
+      if (!glyph_props[i].sva.fClusterStart)
+	vis_clusters[i] = vis_clusters[i + 1];
+  }
 
 #undef utf16_index
 
@@ -421,9 +432,6 @@ retry:
   buffer->len = 0;
   for (unsigned int i = 0; i < glyphs_len; i++)
   {
-    if (glyph_props[i].sva.fZeroWidth)
-      continue;
-
     hb_glyph_info_t *info = &buffer->info[buffer->len++];
 
     info->codepoint = glyphs[i];
commit 11e51993ab562d4c7460eb7c43d0e97404e628e7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Sep 19 09:58:55 2011 -0400

    [util] Move font-size into view-options

diff --git a/util/options.cc b/util/options.cc
index fe2feaf..96d131f 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -336,6 +336,7 @@ view_options_t::add_options (option_parser_t *parser)
     {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: "DEFAULT_FORE")",	"red/#rrggbb/#rrggbbaa"},
     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
+    {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
     {NULL}
   };
   parser->add_group (entries,
@@ -371,7 +372,6 @@ font_options_t::add_options (option_parser_t *parser)
   {
     {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Font file-name",					"filename"},
     {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Face index (default: 0)",                              "index"},
-    {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
     {NULL}
   };
   parser->add_group (entries,
diff --git a/util/options.hh b/util/options.hh
index a101f7d..e8747a6 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -77,6 +77,7 @@ struct option_parser_t
 #define DEFAULT_MARGIN 18
 #define DEFAULT_FORE "#000000"
 #define DEFAULT_BACK "#FFFFFF"
+#define DEFAULT_FONT_SIZE 36
 
 struct view_options_t : option_group_t
 {
@@ -86,6 +87,7 @@ struct view_options_t : option_group_t
     back = DEFAULT_BACK;
     line_space = 0;
     margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
+    font_size = DEFAULT_FONT_SIZE;
 
     add_options (parser);
   }
@@ -99,6 +101,7 @@ struct view_options_t : option_group_t
   struct margin_t {
     double t, r, b, l;
   } margin;
+  double font_size;
 };
 
 
@@ -142,14 +145,11 @@ struct shape_options_t : option_group_t
 };
 
 
-#define DEFAULT_FONT_SIZE 36
-
 struct font_options_t : option_group_t
 {
   font_options_t (option_parser_t *parser) {
     font_file = NULL;
     face_index = 0;
-    font_size = DEFAULT_FONT_SIZE;
 
     font = NULL;
 
@@ -165,7 +165,6 @@ struct font_options_t : option_group_t
 
   const char *font_file;
   int face_index;
-  double font_size;
 
   private:
   mutable hb_font_t *font;
diff --git a/util/view-cairo.cc b/util/view-cairo.cc
index 9ca44c8..c6278f1 100644
--- a/util/view-cairo.cc
+++ b/util/view-cairo.cc
@@ -80,7 +80,7 @@ void
 view_cairo_t::init (const font_options_t *font_opts)
 {
   lines = g_array_new (FALSE, FALSE, sizeof (line_t));
-  scale = double (font_opts->font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
+  scale = double (font_size) / hb_face_get_upem (hb_font_get_face (font_opts->get_font ()));
 }
 
 void
@@ -217,7 +217,7 @@ view_cairo_t::create_scaled_font (const font_options_t *font_opts)
 
   cairo_matrix_init_identity (&ctm);
   cairo_matrix_init_scale (&font_matrix,
-			   font_opts->font_size, font_opts->font_size);
+			   font_size, font_size);
   font_options = cairo_font_options_create ();
   cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
   cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);



More information about the HarfBuzz mailing list