2008-01-08 Xan Lopez <xan@gnome.org>
[WebKit-https.git] / WebCore / platform / graphics / gtk / FontGtk.cpp
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  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
30  */
31
32 #include "config.h"
33 #include "Font.h"
34
35 #include "GraphicsContext.h"
36 #include "NotImplemented.h"
37 #include "SimpleFontData.h"
38
39 #include <cairo.h>
40 #include <pango/pango.h>
41 #include <pango/pangocairo.h>
42
43 namespace WebCore {
44
45 #define IS_HIGH_SURROGATE(u)  ((UChar)(u) >= (UChar)0xd800 && (UChar)(u) <= (UChar)0xdbff)
46 #define IS_LOW_SURROGATE(u)  ((UChar)(u) >= (UChar)0xdc00 && (UChar)(u) <= (UChar)0xdfff)
47
48 static void utf16_to_utf8(const UChar* aText, gint aLength, char* &text, gint &length)
49 {
50   gboolean need_copy = FALSE;
51   int i;
52
53   for (i = 0; i < aLength; i++) {
54     if (!aText[i] || IS_LOW_SURROGATE(aText[i])) {
55       need_copy = TRUE;
56       break;
57     }
58     else if (IS_HIGH_SURROGATE(aText[i])) {
59       if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
60         i++;
61       else {
62         need_copy = TRUE;
63         break;
64       }
65     }
66   }
67
68   if (need_copy) {
69
70     /* Pango doesn't correctly handle nuls.  We convert them to 0xff. */
71     /* Also "validate" UTF-16 text to make sure conversion doesn't fail. */
72
73     UChar* p = (UChar*)g_memdup(aText, aLength * sizeof(aText[0]));
74
75     /* don't need to reset i */
76     for (i = 0; i < aLength; i++) {
77       if (!p[i] || IS_LOW_SURROGATE(p[i]))
78         p[i] = 0xFFFD;
79       else if (IS_HIGH_SURROGATE(p[i])) {
80         if (i < aLength - 1 && IS_LOW_SURROGATE(aText[i+1]))
81           i++;
82         else
83           p[i] = 0xFFFD;
84       }
85     }
86
87     aText = p;
88   }
89
90   glong items_written;
91   text = g_utf16_to_utf8(aText, aLength, NULL, &items_written, NULL);
92   length = items_written;
93
94   if (need_copy)
95     g_free((gpointer)aText);
96
97 }
98
99 static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
100 {
101     gchar* utf8 = 0;
102     gint new_length = 0;
103     utf16_to_utf8(characters, length, utf8, new_length);
104     if (!utf8)
105         return NULL;
106
107     if (from > 0) {
108         // discard the first 'from' characters
109         // FIXME: we should do this before the conversion probably
110         gchar* str_left = g_utf8_offset_to_pointer(utf8, from);
111         gchar* tmp = g_strdup(str_left);
112         g_free(utf8);
113         utf8 = tmp;
114     }
115
116     gchar* pos = utf8;
117     gint len = strlen(pos);
118     GString* ret = g_string_new_len(NULL, len);
119
120     // replace line break by space
121     while (len > 0) {
122         gint index, start;
123         pango_find_paragraph_boundary(pos, len, &index, &start);
124         g_string_append_len(ret, pos, index);
125         if (index == start)
126             break;
127         g_string_append_c(ret, ' ');
128         pos += start;
129         len -= start;
130     }
131     return g_string_free(ret, FALSE);
132 }
133
134 static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout* layout)
135 {
136     PangoAttrList* list = pango_attr_list_new();
137     PangoAttribute* attr;
138
139     attr = pango_attr_size_new_absolute((int)(font->size() * PANGO_SCALE));
140     attr->end_index = G_MAXUINT;
141     pango_attr_list_insert_before(list, attr);
142
143     if (!run.spacingDisabled()) {
144         attr = pango_attr_letter_spacing_new(font->letterSpacing() * PANGO_SCALE);
145         attr->end_index = G_MAXUINT;
146         pango_attr_list_insert_before(list, attr);
147     }
148
149     // Pango does not yet support synthesising small caps
150     // See http://bugs.webkit.org/show_bug.cgi?id=15610
151
152     pango_layout_set_attributes(layout, list);
153     pango_attr_list_unref(list);
154
155     pango_layout_set_auto_dir(layout, FALSE);
156
157     PangoContext* pangoContext = pango_layout_get_context(layout);
158     PangoDirection direction = run.rtl() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
159     pango_context_set_base_dir(pangoContext, direction);
160 }
161
162 void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
163                       int from, int numGlyphs, const FloatPoint& point) const
164 {
165     cairo_t* cr = graphicsContext->platformContext();
166     cairo_save(cr);
167
168     // Set the text color to use for drawing.
169     float red, green, blue, alpha;
170     Color penColor = graphicsContext->fillColor();
171     penColor.getRGBA(red, green, blue, alpha);
172     cairo_set_source_rgba(cr, red, green, blue, alpha);
173
174     font->setFont(cr);
175
176     GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from);
177
178     float offset = point.x();
179
180     for (int i = 0; i < numGlyphs; i++) {
181         glyphs[i].x = offset;
182         glyphs[i].y = point.y();
183         offset += glyphBuffer.advanceAt(from + i);
184     }
185     cairo_show_glyphs(cr, glyphs, numGlyphs);
186
187     cairo_restore(cr);
188 }
189
190 void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
191 {
192     cairo_t* cr = context->platformContext();
193     cairo_save(cr);
194
195     PangoLayout* layout = pango_cairo_create_layout(cr);
196
197     gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), from, to);
198     pango_layout_set_text(layout, utf8, -1);
199     g_free(utf8);
200
201     setPangoAttributes(this, run, layout);
202
203     // Set the text color to use for drawing.
204     float red, green, blue, alpha;
205     Color penColor = context->fillColor();
206     penColor.getRGBA(red, green, blue, alpha);
207     cairo_set_source_rgba(cr, red, green, blue, alpha);
208
209     // Our layouts are single line
210     cairo_move_to(cr, point.x(), point.y());
211     PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);
212     pango_cairo_show_layout_line(cr, layoutLine);
213
214     g_object_unref(layout);
215     cairo_restore(cr);
216 }
217
218 // FIXME: we should create the layout with our actual context, but it seems
219 // we can't access it from here
220 static PangoLayout* getDefaultPangoLayout(const TextRun& run)
221 {
222     PangoFontMap* map = pango_cairo_font_map_get_default();
223     PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(map));
224     PangoLayout* layout = pango_layout_new(pangoContext);
225     g_object_unref(pangoContext);
226
227     return layout;
228 }
229
230 float Font::floatWidthForComplexText(const TextRun& run) const
231 {
232     if (run.length() == 0)
233         return 0.0f;
234
235     PangoLayout* layout = getDefaultPangoLayout(run);
236     setPangoAttributes(this, run, layout);
237
238     gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
239     pango_layout_set_text(layout, utf8, -1);
240     g_free(utf8);
241
242     int layoutWidth;
243     pango_layout_get_size(layout, &layoutWidth, 0);
244     float width = (float)layoutWidth / (double)PANGO_SCALE;
245     g_object_unref(layout);
246
247     return width;
248 }
249
250 int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
251 {
252     PangoLayout* layout = getDefaultPangoLayout(run);
253     setPangoAttributes(this, run, layout);
254
255     gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
256     pango_layout_set_text(layout, utf8, -1);
257
258     int index, trailing;
259     pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing);
260     glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index);
261     g_free(utf8);
262     g_object_unref(layout);
263
264     return offset;
265 }
266
267 FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
268 {
269     notImplemented();
270     return FloatRect();
271 }
272
273 }