2011-07-05 Igor Oliveira <igor.oliveira@openbossa.org>
[WebKit-https.git] / Source / WebCore / platform / graphics / qt / FontQt.cpp
1 /*
2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3     Copyright (C) 2008, 2010 Holger Hans Peter Freyther
4     Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
5
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Library General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Library General Public License for more details.
15
16     You should have received a copy of the GNU Library General Public License
17     along with this library; see the file COPYING.LIB.  If not, write to
18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19     Boston, MA 02110-1301, USA.
20 */
21
22 #include "config.h"
23 #include "Font.h"
24
25 #include "AffineTransform.h"
26 #include "FontDescription.h"
27 #include "FontFallbackList.h"
28 #include "FontSelector.h"
29 #if HAVE(QRAWFONT)
30 #include "GlyphBuffer.h"
31 #endif
32 #include "Gradient.h"
33 #include "GraphicsContext.h"
34 #include "NotImplemented.h"
35 #include "Pattern.h"
36 #include "ShadowBlur.h"
37 #include "TextRun.h"
38
39 #include <QBrush>
40 #include <QFontInfo>
41 #include <QFontMetrics>
42 #include <QPainter>
43 #include <QPainterPath>
44 #include <QPen>
45 #include <QTextLayout>
46 #include <qalgorithms.h>
47 #include <qdebug.h>
48
49 #include <limits.h>
50
51 namespace WebCore {
52
53 static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1)
54 {
55     if (len < 0)
56         len = string.length() - start;
57     Q_ASSERT(start + len <= string.length());
58
59     // We don't detach. This assumes the WebCore string data will stay valid for the
60     // lifetime of the QString we pass back, since we don't ref the WebCore string.
61     return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters() + start), len);
62 }
63
64 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
65 {
66     int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
67     if (style.expansion())
68         flags |= Qt::TextJustificationForced;
69     layout->setFlags(flags);
70     layout->beginLayout();
71     QTextLine line = layout->createLine();
72     line.setLineWidth(INT_MAX/256);
73     if (style.expansion())
74         line.setLineWidth(line.naturalTextWidth() + style.expansion());
75     layout->endLayout();
76     return line;
77 }
78
79 static QPen fillPenForContext(GraphicsContext* ctx)
80 {
81     if (ctx->fillGradient()) {
82         QBrush brush(*ctx->fillGradient()->platformGradient());
83         brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
84         return QPen(brush, 0);
85     }
86
87     if (ctx->fillPattern()) {
88         AffineTransform affine;
89         return QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
90     }
91
92     return QPen(QColor(ctx->fillColor()));
93 }
94
95 static QPen strokePenForContext(GraphicsContext* ctx)
96 {
97     if (ctx->strokeGradient()) {
98         QBrush brush(*ctx->strokeGradient()->platformGradient());
99         brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
100         return QPen(brush, ctx->strokeThickness());
101     }
102
103     if (ctx->strokePattern()) {
104         AffineTransform affine;
105         QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
106         return QPen(brush, ctx->strokeThickness());
107     }
108
109     return QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
110 }
111
112 static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
113 {
114     if (to < 0)
115         to = run.length();
116
117     QPainter *p = ctx->platformContext();
118
119     QPen textFillPen;
120     if (ctx->textDrawingMode() & TextModeFill)
121         textFillPen = fillPenForContext(ctx);
122
123     QPen textStrokePen;
124     if (ctx->textDrawingMode() & TextModeStroke)
125         textStrokePen = strokePenForContext(ctx);
126
127     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
128     QString string = fromRawDataWithoutRef(sanitized);
129     QPointF pt(point.x(), point.y());
130
131     if (from > 0 || to < run.length()) {
132         if (isComplexText) {
133             QTextLayout layout(string, font);
134             QTextLine line = setupLayout(&layout, run);
135             float x1 = line.cursorToX(from);
136             float x2 = line.cursorToX(to);
137             if (x2 < x1)
138                 qSwap(x1, x2);
139
140             QFontMetrics fm(font);
141             int ascent = fm.ascent();
142             QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
143             QRectF clip = boundingRect;
144
145             ShadowBlur* ctxShadow = ctx->shadowBlur();
146             if (ctxShadow->type() != ShadowBlur::NoShadow) {
147                 const QPointF shadowOffset(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height());
148                 qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
149                 if (shadowOffset.x() > 0)
150                     dx2 = shadowOffset.x();
151                 else
152                     dx1 = -shadowOffset.x();
153                 if (shadowOffset.y() > 0)
154                     dy2 = shadowOffset.y();
155                 else
156                     dy1 = -shadowOffset.y();
157                 // expand the clip rect to include the text shadow as well
158                 const float blurDistance = ctx->state().shadowBlur;
159                 clip.adjust(dx1, dx2, dy1, dy2);
160                 clip.adjust(-blurDistance, -blurDistance, blurDistance, blurDistance);
161             }
162             p->save();
163             p->setClipRect(clip.toRect(), Qt::IntersectClip);
164             pt.setY(pt.y() - ascent);
165
166             if (ctxShadow->type() != ShadowBlur::NoShadow) {
167                 ShadowBlur* ctxShadow = ctx->shadowBlur();
168                 if (ctxShadow->type() != ShadowBlur::BlurShadow
169                     && (!ctxShadow->shadowsIgnoreTransforms() || ctx->getCTM().isIdentity())) {
170                     p->save();
171                     p->setPen(ctx->state().shadowColor);
172                     p->translate(QPointF(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height()));
173                     line.draw(p, pt);
174                     p->restore();
175                 } else {
176                     GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
177                     if (shadowContext) {
178                         QPainter* shadowPainter = shadowContext->platformContext();
179                         // Since it will be blurred anyway, we don't care about render hints.
180                         shadowPainter->setFont(p->font());
181                         shadowPainter->setPen(ctx->state().shadowColor);
182                         line.draw(shadowPainter, pt);
183                         ctxShadow->endShadowLayer(ctx);
184                     }
185                 }
186             }
187             p->setPen(textFillPen);
188             line.draw(p, pt);
189             p->restore();
190             return;
191         }
192         int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
193         pt.setX(pt.x() + skipWidth);
194         string = fromRawDataWithoutRef(sanitized, from, to - from);
195     }
196
197     p->setFont(font);
198
199     int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
200     if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
201         flags |= Qt::TextBypassShaping;
202
203     QPainterPath textStrokePath;
204     if (ctx->textDrawingMode() & TextModeStroke)
205         textStrokePath.addText(pt, font, string);
206
207     ShadowBlur* ctxShadow = ctx->shadowBlur();
208     if (ctx->hasShadow() && ctxShadow->type() != ShadowBlur::NoShadow) {
209         if (ctx->textDrawingMode() & TextModeFill) {
210             if (ctxShadow->type() != ShadowBlur::BlurShadow) {
211                 p->save();
212                 p->setPen(ctx->state().shadowColor);
213                 p->translate(QPointF(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height()));
214                 p->drawText(pt, string, flags, run.expansion());
215                 p->restore();
216             } else {
217                 QFontMetrics fm(font);
218                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
219                 GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
220                 if (shadowContext) {
221                     QPainter* shadowPainter = shadowContext->platformContext();
222                     // Since it will be blurred anyway, we don't care about render hints.
223                     shadowPainter->setFont(p->font());
224                     shadowPainter->setPen(ctx->state().shadowColor);
225                     shadowPainter->drawText(pt, string, flags, run.expansion());
226                     ctxShadow->endShadowLayer(ctx);
227                 }
228             }
229         } else if (ctx->textDrawingMode() & TextModeStroke) {
230             if (ctxShadow->type() != ShadowBlur::BlurShadow) {
231                 const QPointF shadowOffset(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height());
232                 p->translate(shadowOffset);
233                 p->strokePath(textStrokePath, QPen(ctx->state().shadowColor));
234                 p->translate(-shadowOffset);
235             } else {
236                 QFontMetrics fm(font);
237                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
238                 GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
239                 if (shadowContext) {
240                     QPainter* shadowPainter = shadowContext->platformContext();
241                     // Since it will be blurred anyway, we don't care about render hints.
242                     shadowPainter->setFont(p->font());
243                     shadowPainter->strokePath(textStrokePath, QPen(ctx->state().shadowColor));
244                     ctxShadow->endShadowLayer(ctx);
245                 }
246             }
247         }
248     }
249
250     if (ctx->textDrawingMode() & TextModeStroke)
251         p->strokePath(textStrokePath, textStrokePen);
252
253     if (ctx->textDrawingMode() & TextModeFill) {
254         QPen previousPen = p->pen();
255         p->setPen(textFillPen);
256         p->drawText(pt, string, flags, run.expansion());
257         p->setPen(previousPen);
258     }
259 }
260
261 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
262 {
263     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
264 }
265
266 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
267 {
268     if (!primaryFont()->platformData().size())
269         return 0;
270
271     if (!run.length())
272         return 0;
273
274     if (run.length() == 1 && treatAsSpace(run[0]))
275         return QFontMetrics(font()).width(space) + run.expansion();
276
277     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
278     QString string = fromRawDataWithoutRef(sanitized);
279
280     int w = QFontMetrics(font()).width(string);
281     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
282     if (treatAsSpace(run[0]))
283         w -= m_wordSpacing;
284
285     return w + run.expansion();
286 }
287
288 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
289 {
290     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
291     QString string = fromRawDataWithoutRef(sanitized);
292
293     QTextLayout layout(string, font());
294     QTextLine line = setupLayout(&layout, run);
295     return line.xToCursor(position);
296 }
297
298 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
299 {
300     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
301     QString string = fromRawDataWithoutRef(sanitized);
302
303     QTextLayout layout(string, font());
304     QTextLine line = setupLayout(&layout, run);
305
306     float x1 = line.cursorToX(from);
307     float x2 = line.cursorToX(to);
308     if (x2 < x1)
309         qSwap(x1, x2);
310
311     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
312 }
313
314 bool Font::canReturnFallbackFontsForComplexText()
315 {
316     return false;
317 }
318
319 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
320 {
321     notImplemented();
322 }
323
324 #if HAVE(QRAWFONT)
325 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
326 {
327     if (context->paintingDisabled())
328         return;
329
330     bool shouldFill = context->textDrawingMode() & TextModeFill;
331     bool shouldStroke = context->textDrawingMode() & TextModeStroke;
332
333     // Stroking text should always take the complex path.
334     ASSERT(!shouldStroke);
335
336     // Shadowed text should always take the complex path.
337     ASSERT(context->contextShadow()->type() == ShadowBlur::NoShadow);
338
339     if (!shouldFill && !shouldStroke)
340         return;
341
342     QVector<quint32> glyphIndexes;
343     QVector<QPointF> positions;
344
345     glyphIndexes.reserve(numGlyphs);
346     positions.reserve(numGlyphs);
347
348     float width = 0;
349
350     for (int i = 0; i < numGlyphs; ++i) {
351         Glyph glyph = glyphBuffer.glyphAt(from + i);
352         float advance = glyphBuffer.advanceAt(from + i);
353         if (!glyph)
354             continue;
355         glyphIndexes.append(glyph);
356         positions.append(QPointF(width, 0));
357         width += advance;
358     }
359
360     QRawFont rawFont(fontData->platformData().rawFont());
361
362     QGlyphRun qtGlyphs;
363     qtGlyphs.setGlyphIndexes(glyphIndexes);
364     qtGlyphs.setPositions(positions);
365     qtGlyphs.setRawFont(rawFont);
366
367     QPainter* painter = context->platformContext();
368
369     ContextShadow* shadow = context->contextShadow();
370     switch (shadow->type()) {
371     case ContextShadow::SolidShadow: {
372         QPen previousPen = painter->pen();
373         painter->setPen(shadow->m_color);
374         painter->translate(shadow->offset());
375         painter->drawGlyphRun(point, qtGlyphs);
376         painter->translate(-shadow->offset());
377         painter->setPen(previousPen);
378         break;
379     }
380     case ContextShadow::BlurShadow: {
381         qreal height = rawFont.ascent() + rawFont.descent() + 1;
382         QRectF boundingRect(point.x(), point.y() - rawFont.ascent(), width, height);
383         GraphicsContext* shadowContext = shadow->beginShadowLayer(context, boundingRect);
384         if (shadowContext) {
385             QPainter* shadowPainter = shadowContext->platformContext();
386             shadowPainter->setPen(shadow->m_color);
387             shadowPainter->drawGlyphRun(point, qtGlyphs);
388             shadow->endShadowLayer(context);
389         }
390         break;
391     }
392     case ContextShadow::NoShadow:
393         break;
394     default:
395         ASSERT_NOT_REACHED();
396         break;
397     }
398
399     QPen previousPen = painter->pen();
400     painter->setPen(fillPenForContext(context));
401     painter->drawGlyphRun(point, qtGlyphs);
402     painter->setPen(previousPen);
403 }
404
405 bool Font::canExpandAroundIdeographsInComplexText()
406 {
407     return false;
408 }
409
410 #else // !HAVE(QRAWFONT)
411
412 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
413 {
414     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
415 }
416
417 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
418 {
419     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
420     QString string = fromRawDataWithoutRef(sanitized);
421
422     QFontMetrics fm(font());
423     float delta = position;
424     int curPos = 0;
425     do {
426         float charWidth = fm.width(string[curPos]);
427         delta -= charWidth;
428         if (includePartialGlyphs) {
429             if (delta + charWidth / 2 <= 0)
430                 break;
431         } else {
432             if (delta + charWidth <= 0)
433                 break;
434         }
435     } while (++curPos < string.size());
436
437     return curPos;
438 }
439
440
441 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
442 {
443     if (!primaryFont()->platformData().size())
444         return 0;
445
446     if (!run.length())
447         return 0;
448
449     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
450     QString string = fromRawDataWithoutRef(sanitized);
451
452     int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
453
454     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
455     if (treatAsSpace(run[0]))
456         w -= m_wordSpacing;
457
458     return w + run.expansion();
459 }
460
461
462 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
463 {
464     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
465     QString wholeText = fromRawDataWithoutRef(sanitized);
466     QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from));
467
468     int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
469     int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
470
471     return FloatRect(pt.x() + startX, pt.y(), width, h);
472 }
473
474 bool Font::canExpandAroundIdeographsInComplexText()
475 {
476     return false;
477 }
478
479 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
480 {
481     notImplemented();
482     return true;
483 }
484
485 int Font::emphasisMarkAscent(const AtomicString&) const
486 {
487     notImplemented();
488     return 0;
489 }
490
491 int Font::emphasisMarkDescent(const AtomicString&) const
492 {
493     notImplemented();
494     return 0;
495 }
496
497 int Font::emphasisMarkHeight(const AtomicString&) const
498 {
499     notImplemented();
500     return 0;
501 }
502
503 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
504 {
505     notImplemented();
506 }
507 #endif // HAVE(QRAWFONT)
508
509 QFont Font::font() const
510 {
511     QFont f = primaryFont()->getQtFont();
512     if (m_letterSpacing != 0)
513         f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
514     if (m_wordSpacing != 0)
515         f.setWordSpacing(m_wordSpacing);
516     return f;
517 }
518
519
520 }
521