root/trunk/WebCore/platform/graphics/gtk/FontGtk.cpp

Revision 37926, 11.8 KB (checked in by alp@webkit.org, 5 weeks ago)

2008-10-28 Alp Toker <alp@nuanti.com>

GTK+ build fix for older versions of Pango where PANGO_VERSION_CHECK
isn't defined.

  • platform/graphics/gtk/FontGtk.cpp: (WebCore::getDefaultPangoLayout):
  • platform/graphics/gtk/FontPlatformDataPango.cpp:
  • Property svn:eol-style set to native
Line 
1/*
2 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3 * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
4 * Copyright (c) 2007 Hiroyuki Ikezoe
5 * Copyright (c) 2007 Kouhei Sutou
6 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
7 * Copyright (C) 2008 Xan Lopez <xan@gnome.org>
8 * Copyright (C) 2008 Nuanti Ltd.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "config.h"
34#include "Font.h"
35
36#include "GraphicsContext.h"
37#include "NotImplemented.h"
38#include "SimpleFontData.h"
39
40#include <cairo.h>
41#include <gdk/gdk.h>
42#include <pango/pango.h>
43#include <pango/pangocairo.h>
44#if defined(USE_FREETYPE)
45#include <pango/pangofc-fontmap.h>
46#endif
47
48#if !defined(PANGO_VERSION_CHECK)
49// PANGO_VERSION_CHECK() and pango_layout_get_line_readonly() appeared in 1.5.2
50#define pango_layout_get_line_readonly pango_layout_get_line
51#define PANGO_VERSION_CHECK(major,minor,micro) 0
52#endif
53
54namespace WebCore {
55
56#define IS_HIGH_SURROGATE(u)  ((UChar)(u) >= (UChar)0xd800 && (UChar)(u) <= (UChar)0xdbff)
57#define IS_LOW_SURROGATE(u)  ((UChar)(u) >= (UChar)0xdc00 && (UChar)(u) <= (UChar)0xdfff)
58
59static void utf16_to_utf8(const UChar* aText, gint aLength, char* &text, gint &length)
60{
61  gboolean need_copy = FALSE;
62  int i;
63
64  for (i = 0; i < aLength; i++) {
65    if (!aText[i] || IS_LOW_SURROGATE(aText[i])) {
66      need_copy = TRUE;
67      break;
68    }
69    else if (IS_HIGH_SURROGATE(aText[i])) {
70      if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
71        i++;
72      else {
73        need_copy = TRUE;
74        break;
75      }
76    }
77  }
78
79  if (need_copy) {
80
81    /* Pango doesn't correctly handle nuls.  We convert them to 0xff. */
82    /* Also "validate" UTF-16 text to make sure conversion doesn't fail. */
83
84    UChar* p = (UChar*)g_memdup(aText, aLength * sizeof(aText[0]));
85
86    /* don't need to reset i */
87    for (i = 0; i < aLength; i++) {
88      if (!p[i] || IS_LOW_SURROGATE(p[i]))
89        p[i] = 0xFFFD;
90      else if (IS_HIGH_SURROGATE(p[i])) {
91        if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
92          i++;
93        else
94          p[i] = 0xFFFD;
95      }
96    }
97
98    aText = p;
99  }
100
101  glong items_written;
102  text = g_utf16_to_utf8(reinterpret_cast<const gunichar2*>(aText), aLength, NULL, &items_written, NULL);
103  length = items_written;
104
105  if (need_copy)
106    g_free((gpointer)aText);
107
108}
109
110static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
111{
112    gchar* utf8 = 0;
113    gint new_length = 0;
114    utf16_to_utf8(characters, length, utf8, new_length);
115    if (!utf8)
116        return NULL;
117
118    if (from > 0) {
119        // discard the first 'from' characters
120        // FIXME: we should do this before the conversion probably
121        gchar* str_left = g_utf8_offset_to_pointer(utf8, from);
122        gchar* tmp = g_strdup(str_left);
123        g_free(utf8);
124        utf8 = tmp;
125    }
126
127    gchar* pos = utf8;
128    gint len = strlen(pos);
129    GString* ret = g_string_new_len(NULL, len);
130
131    // replace line break by space
132    while (len > 0) {
133        gint index, start;
134        pango_find_paragraph_boundary(pos, len, &index, &start);
135        g_string_append_len(ret, pos, index);
136        if (index == start)
137            break;
138        g_string_append_c(ret, ' ');
139        pos += start;
140        len -= start;
141    }
142    return g_string_free(ret, FALSE);
143}
144
145static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout* layout)
146{
147#if defined(USE_FREETYPE)
148    if (font->primaryFont()->m_font.m_pattern) {
149        PangoFontDescription* desc = pango_fc_font_description_from_pattern(font->primaryFont()->m_font.m_pattern, FALSE);
150        pango_layout_set_font_description(layout, desc);
151        pango_font_description_free(desc);
152    }
153#elif defined(USE_PANGO)
154    if (font->primaryFont()->m_font.m_font) {
155        PangoFontDescription* desc = pango_font_describe(font->primaryFont()->m_font.m_font);
156        pango_layout_set_font_description(layout, desc);
157        pango_font_description_free(desc);
158    }
159#endif
160
161    pango_layout_set_auto_dir(layout, FALSE);
162
163    PangoContext* pangoContext = pango_layout_get_context(layout);
164    PangoDirection direction = run.rtl() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
165    pango_context_set_base_dir(pangoContext, direction);
166    PangoAttrList* list = pango_attr_list_new();
167    PangoAttribute* attr;
168
169    attr = pango_attr_size_new_absolute(font->pixelSize() * PANGO_SCALE);
170    attr->end_index = G_MAXUINT;
171    pango_attr_list_insert_before(list, attr);
172
173    if (!run.spacingDisabled()) {
174        attr = pango_attr_letter_spacing_new(font->letterSpacing() * PANGO_SCALE);
175        attr->end_index = G_MAXUINT;
176        pango_attr_list_insert_before(list, attr);
177    }
178
179    // Pango does not yet support synthesising small caps
180    // See http://bugs.webkit.org/show_bug.cgi?id=15610
181
182    pango_layout_set_attributes(layout, list);
183    pango_attr_list_unref(list);
184}
185
186void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
187{
188    cairo_t* cr = context->platformContext();
189    cairo_save(cr);
190    cairo_translate(cr, point.x(), point.y());
191
192    PangoLayout* layout = pango_cairo_create_layout(cr);
193    setPangoAttributes(this, run, layout);
194
195    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
196    pango_layout_set_text(layout, utf8, -1);
197
198    // Our layouts are single line
199    PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);
200
201    GdkRegion* partialRegion = NULL;
202    if (to - from != run.length()) {
203        // Clip the region of the run to be rendered
204        char* start = g_utf8_offset_to_pointer(utf8, from);
205        char* end = g_utf8_offset_to_pointer(start, to - from);
206        int ranges[] = {start - utf8, end - utf8};
207        partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1);
208        gdk_region_shrink(partialRegion, 0, -pixelSize());
209    }
210
211    Color fillColor = context->fillColor();
212    float red, green, blue, alpha;
213
214    // Text shadow, inspired by FontMac
215    IntSize shadowSize;
216    int shadowBlur = 0;
217    Color shadowColor;
218    bool hasShadow = context->textDrawingMode() == cTextFill &&
219        context->getShadow(shadowSize, shadowBlur, shadowColor);
220
221    // TODO: Blur support
222    if (hasShadow) {
223        // Disable graphics context shadows (not yet implemented) and paint them manually
224        context->clearShadow();
225        Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
226        cairo_save(cr);
227
228        shadowFillColor.getRGBA(red, green, blue, alpha);
229        cairo_set_source_rgba(cr, red, green, blue, alpha);
230
231        cairo_translate(cr, shadowSize.width(), shadowSize.height());
232
233        if (partialRegion) {
234            gdk_cairo_region(cr, partialRegion);
235            cairo_clip(cr);
236        }
237
238        pango_cairo_show_layout_line(cr, layoutLine);
239
240        cairo_restore(cr);
241    }
242
243    fillColor.getRGBA(red, green, blue, alpha);
244    cairo_set_source_rgba(cr, red, green, blue, alpha);
245
246    if (partialRegion) {
247        gdk_cairo_region(cr, partialRegion);
248        cairo_clip(cr);
249    }
250
251    pango_cairo_show_layout_line(cr, layoutLine);
252
253    if (context->textDrawingMode() & cTextStroke) {
254        Color strokeColor = context->strokeColor();
255        strokeColor.getRGBA(red, green, blue, alpha);
256        cairo_set_source_rgba(cr, red, green, blue, alpha);
257        pango_cairo_layout_line_path(cr, layoutLine);
258        cairo_set_line_width(cr, context->strokeThickness());
259        cairo_stroke(cr);
260    }
261
262    // Re-enable the platform shadow we disabled earlier
263    if (hasShadow)
264        context->setShadow(shadowSize, shadowBlur, shadowColor);
265
266    // Pango sometimes leaves behind paths we don't want
267    cairo_new_path(cr);
268
269    if (partialRegion)
270        gdk_region_destroy(partialRegion);
271
272    g_free(utf8);
273    g_object_unref(layout);
274
275    cairo_restore(cr);
276}
277
278// We should create the layout with our actual context but we can't access it from here.
279static PangoLayout* getDefaultPangoLayout(const TextRun& run)
280{
281    static PangoFontMap* map = pango_cairo_font_map_get_default();
282#if PANGO_VERSION_CHECK(1,21,5)
283    static PangoContext* pangoContext = pango_font_map_create_context(map);
284#else
285    // Deprecated in Pango 1.21.
286    static PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(map));
287#endif
288    PangoLayout* layout = pango_layout_new(pangoContext);
289
290    return layout;
291}
292
293float Font::floatWidthForComplexText(const TextRun& run) const
294{
295    if (run.length() == 0)
296        return 0.0f;
297
298    PangoLayout* layout = getDefaultPangoLayout(run);
299    setPangoAttributes(this, run, layout);
300
301    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
302    pango_layout_set_text(layout, utf8, -1);
303
304    int width;
305    pango_layout_get_pixel_size(layout, &width, 0);
306
307    g_free(utf8);
308    g_object_unref(layout);
309
310    return width;
311}
312
313int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
314{
315    PangoLayout* layout = getDefaultPangoLayout(run);
316    setPangoAttributes(this, run, layout);
317
318    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
319    pango_layout_set_text(layout, utf8, -1);
320
321    int index, trailing;
322    pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing);
323    glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index);
324    if (includePartialGlyphs)
325        offset += trailing;
326
327    g_free(utf8);
328    g_object_unref(layout);
329
330    return offset;
331}
332
333FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
334{
335    PangoLayout* layout = getDefaultPangoLayout(run);
336    setPangoAttributes(this, run, layout);
337
338    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
339    pango_layout_set_text(layout, utf8, -1);
340
341    char* start = g_utf8_offset_to_pointer(utf8, from);
342    char* end = g_utf8_offset_to_pointer(start, to - from);
343
344    if (run.ltr()) {
345        from = start - utf8;
346        to = end - utf8;
347    } else {
348        from = end - utf8;
349        to = start - utf8;
350    }
351
352    PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);
353    int x_pos;
354
355    x_pos = 0;
356    if (from < layoutLine->length)
357        pango_layout_line_index_to_x(layoutLine, from, FALSE, &x_pos);
358    float beforeWidth = PANGO_PIXELS_FLOOR(x_pos);
359
360    x_pos = 0;
361    if (run.ltr() || to < layoutLine->length)
362        pango_layout_line_index_to_x(layoutLine, to, FALSE, &x_pos);
363    float afterWidth = PANGO_PIXELS(x_pos);
364
365    g_free(utf8);
366    g_object_unref(layout);
367
368    return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h);
369}
370
371}
Note: See TracBrowser for help on using the browser.