2008-01-08 Xan Lopez <xan@gnome.org>
authoralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Jan 2008 04:45:32 +0000 (04:45 +0000)
committeralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Jan 2008 04:45:32 +0000 (04:45 +0000)
        Reviewed by Alp Toker.

        http://bugs.webkit.org/show_bug.cgi?id=15610
        [GTK] Text rendering using Pango

        Use Pango to render Complex path text.

        * platform/graphics/gtk/FontGtk.cpp:
        (WebCore::utf16_to_utf8):
        (WebCore::convertUniCharToUTF8):
        (WebCore::setPangoAttributes):
        (WebCore::Font::drawGlyphs):
        (WebCore::Font::drawComplexText):
        (WebCore::Font::floatWidthForComplexText):
        (WebCore::Font::offsetForPositionForComplexText):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29334 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/graphics/gtk/FontGtk.cpp

index 78d4fffa0d8366777c50d313a0c41e4de650a7df..52fc37336b99bf3cdfd9f6e2509fc6212bd0597e 100644 (file)
@@ -1,3 +1,21 @@
+2008-01-08  Xan Lopez  <xan@gnome.org>
+
+        Reviewed by Alp Toker.
+
+        http://bugs.webkit.org/show_bug.cgi?id=15610
+        [GTK] Text rendering using Pango
+
+        Use Pango to render Complex path text.
+
+        * platform/graphics/gtk/FontGtk.cpp:
+        (WebCore::utf16_to_utf8):
+        (WebCore::convertUniCharToUTF8):
+        (WebCore::setPangoAttributes):
+        (WebCore::Font::drawGlyphs):
+        (WebCore::Font::drawComplexText):
+        (WebCore::Font::floatWidthForComplexText):
+        (WebCore::Font::offsetForPositionForComplexText):
+
 2008-01-08  Timothy Hatcher  <timothy@apple.com>
 
         Reviewed by Darin Adler.
index 3b3a2284008aa9628f89002fe2bfbc9e0943ae6b..807a34e959a5d1b1ca56acf874ad54896b3339d8 100644 (file)
@@ -1,6 +1,10 @@
 /*
  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com 
+ * Copyright (c) 2007 Hiroyuki Ikezoe
+ * Copyright (c) 2007 Kouhei Sutou
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Xan Lopez <xan@gnome.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "GraphicsContext.h"
 #include "NotImplemented.h"
 #include "SimpleFontData.h"
+
 #include <cairo.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
 
 namespace WebCore {
 
+#define IS_HIGH_SURROGATE(u)  ((UChar)(u) >= (UChar)0xd800 && (UChar)(u) <= (UChar)0xdbff)
+#define IS_LOW_SURROGATE(u)  ((UChar)(u) >= (UChar)0xdc00 && (UChar)(u) <= (UChar)0xdfff)
+
+static void utf16_to_utf8(const UChar* aText, gint aLength, char* &text, gint &length)
+{
+  gboolean need_copy = FALSE;
+  int i;
+
+  for (i = 0; i < aLength; i++) {
+    if (!aText[i] || IS_LOW_SURROGATE(aText[i])) {
+      need_copy = TRUE;
+      break;
+    }
+    else if (IS_HIGH_SURROGATE(aText[i])) {
+      if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
+        i++;
+      else {
+        need_copy = TRUE;
+        break;
+      }
+    }
+  }
+
+  if (need_copy) {
+
+    /* Pango doesn't correctly handle nuls.  We convert them to 0xff. */
+    /* Also "validate" UTF-16 text to make sure conversion doesn't fail. */
+
+    UChar* p = (UChar*)g_memdup(aText, aLength * sizeof(aText[0]));
+
+    /* don't need to reset i */
+    for (i = 0; i < aLength; i++) {
+      if (!p[i] || IS_LOW_SURROGATE(p[i]))
+        p[i] = 0xFFFD;
+      else if (IS_HIGH_SURROGATE(p[i])) {
+        if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
+          i++;
+        else
+          p[i] = 0xFFFD;
+      }
+    }
+
+    aText = p;
+  }
+
+  glong items_written;
+  text = g_utf16_to_utf8(aText, aLength, NULL, &items_written, NULL);
+  length = items_written;
+
+  if (need_copy)
+    g_free((gpointer)aText);
+
+}
+
+static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
+{
+    gchar* utf8 = 0;
+    gint new_length = 0;
+    utf16_to_utf8(characters, length, utf8, new_length);
+    if (!utf8)
+        return NULL;
+
+    if (from > 0) {
+        // discard the first 'from' characters
+        // FIXME: we should do this before the conversion probably
+        gchar* str_left = g_utf8_offset_to_pointer(utf8, from);
+        gchar* tmp = g_strdup(str_left);
+        g_free(utf8);
+        utf8 = tmp;
+    }
+
+    gchar* pos = utf8;
+    gint len = strlen(pos);
+    GString* ret = g_string_new_len(NULL, len);
+
+    // replace line break by space
+    while (len > 0) {
+        gint index, start;
+        pango_find_paragraph_boundary(pos, len, &index, &start);
+        g_string_append_len(ret, pos, index);
+        if (index == start)
+            break;
+        g_string_append_c(ret, ' ');
+        pos += start;
+        len -= start;
+    }
+    return g_string_free(ret, FALSE);
+}
+
+static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout* layout)
+{
+    PangoAttrList* list = pango_attr_list_new();
+    PangoAttribute* attr;
+
+    attr = pango_attr_size_new_absolute((int)(font->size() * PANGO_SCALE));
+    attr->end_index = G_MAXUINT;
+    pango_attr_list_insert_before(list, attr);
+
+    if (!run.spacingDisabled()) {
+        attr = pango_attr_letter_spacing_new(font->letterSpacing() * PANGO_SCALE);
+        attr->end_index = G_MAXUINT;
+        pango_attr_list_insert_before(list, attr);
+    }
+
+    // Pango does not yet support synthesising small caps
+    // See http://bugs.webkit.org/show_bug.cgi?id=15610
+
+    pango_layout_set_attributes(layout, list);
+    pango_attr_list_unref(list);
+
+    pango_layout_set_auto_dir(layout, FALSE);
+
+    PangoContext* pangoContext = pango_layout_get_context(layout);
+    PangoDirection direction = run.rtl() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
+    pango_context_set_base_dir(pangoContext, direction);
+}
+
 void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
                       int from, int numGlyphs, const FloatPoint& point) const
 {
-    cairo_t* context = graphicsContext->platformContext();
+    cairo_t* cr = graphicsContext->platformContext();
+    cairo_save(cr);
 
     // Set the text color to use for drawing.
     float red, green, blue, alpha;
     Color penColor = graphicsContext->fillColor();
     penColor.getRGBA(red, green, blue, alpha);
-    cairo_set_source_rgba(context, red, green, blue, alpha);
+    cairo_set_source_rgba(cr, red, green, blue, alpha);
 
-    // This was commented out as it made "some text invisible" but seems to work now.
-    font->setFont(context);
+    font->setFont(cr);
 
-    GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*) glyphBuffer.glyphs(from);
+    GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from);
 
     float offset = point.x();
 
@@ -58,27 +182,89 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo
         glyphs[i].y = point.y();
         offset += glyphBuffer.advanceAt(from + i);
     }
-    cairo_show_glyphs(context, glyphs, numGlyphs);
+    cairo_show_glyphs(cr, glyphs, numGlyphs);
+
+    cairo_restore(cr);
 }
 
-void Font::drawComplexText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const
+void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
 {
-    notImplemented();
+    cairo_t* cr = context->platformContext();
+    cairo_save(cr);
+
+    PangoLayout* layout = pango_cairo_create_layout(cr);
+
+    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), from, to);
+    pango_layout_set_text(layout, utf8, -1);
+    g_free(utf8);
+
+    setPangoAttributes(this, run, layout);
+
+    // Set the text color to use for drawing.
+    float red, green, blue, alpha;
+    Color penColor = context->fillColor();
+    penColor.getRGBA(red, green, blue, alpha);
+    cairo_set_source_rgba(cr, red, green, blue, alpha);
+
+    // Our layouts are single line
+    cairo_move_to(cr, point.x(), point.y());
+    PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);
+    pango_cairo_show_layout_line(cr, layoutLine);
+
+    g_object_unref(layout);
+    cairo_restore(cr);
 }
 
-float Font::floatWidthForComplexText(const TextRun&) const
+// FIXME: we should create the layout with our actual context, but it seems
+// we can't access it from here
+static PangoLayout* getDefaultPangoLayout(const TextRun& run)
 {
-    notImplemented();
-    return 0.0f;
+    PangoFontMap* map = pango_cairo_font_map_get_default();
+    PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(map));
+    PangoLayout* layout = pango_layout_new(pangoContext);
+    g_object_unref(pangoContext);
+
+    return layout;
 }
 
-int Font::offsetForPositionForComplexText(const TextRun&, int, bool) const
+float Font::floatWidthForComplexText(const TextRun& run) const
 {
-    notImplemented();
-    return 0;
+    if (run.length() == 0)
+        return 0.0f;
+
+    PangoLayout* layout = getDefaultPangoLayout(run);
+    setPangoAttributes(this, run, layout);
+
+    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
+    pango_layout_set_text(layout, utf8, -1);
+    g_free(utf8);
+
+    int layoutWidth;
+    pango_layout_get_size(layout, &layoutWidth, 0);
+    float width = (float)layoutWidth / (double)PANGO_SCALE;
+    g_object_unref(layout);
+
+    return width;
+}
+
+int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
+{
+    PangoLayout* layout = getDefaultPangoLayout(run);
+    setPangoAttributes(this, run, layout);
+
+    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
+    pango_layout_set_text(layout, utf8, -1);
+
+    int index, trailing;
+    pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing);
+    glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index);
+    g_free(utf8);
+    g_object_unref(layout);
+
+    return offset;
 }
 
-FloatRect Font::selectionRectForComplexText(const TextRun&, const IntPoint&, int, int, int) const
+FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
 {
     notImplemented();
     return FloatRect();