initial import
[vuplus_webkit] / 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 #if HAVE(QRAWFONT)
46 #include <QPointF>
47 #endif
48 #include <QTextLayout>
49 #include <qalgorithms.h>
50 #include <qdebug.h>
51
52 #include <limits.h>
53
54 namespace WebCore {
55
56 static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1)
57 {
58     if (len < 0)
59         len = string.length() - start;
60     Q_ASSERT(start + len <= string.length());
61
62     // We don't detach. This assumes the WebCore string data will stay valid for the
63     // lifetime of the QString we pass back, since we don't ref the WebCore string.
64     return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters() + start), len);
65 }
66
67 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
68 {
69     int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
70     if (style.expansion())
71         flags |= Qt::TextJustificationForced;
72     layout->setFlags(flags);
73     layout->beginLayout();
74     QTextLine line = layout->createLine();
75     line.setLineWidth(INT_MAX/256);
76     if (style.expansion())
77         line.setLineWidth(line.naturalTextWidth() + style.expansion());
78     layout->endLayout();
79     return line;
80 }
81
82 static QPen fillPenForContext(GraphicsContext* ctx)
83 {
84     if (ctx->fillGradient()) {
85         QBrush brush(*ctx->fillGradient()->platformGradient());
86         brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
87         return QPen(brush, 0);
88     }
89
90     if (ctx->fillPattern()) {
91         AffineTransform affine;
92         return QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
93     }
94
95     return QPen(QColor(ctx->fillColor()));
96 }
97
98 static QPen strokePenForContext(GraphicsContext* ctx)
99 {
100     if (ctx->strokeGradient()) {
101         QBrush brush(*ctx->strokeGradient()->platformGradient());
102         brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
103         return QPen(brush, ctx->strokeThickness());
104     }
105
106     if (ctx->strokePattern()) {
107         AffineTransform affine;
108         QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
109         return QPen(brush, ctx->strokeThickness());
110     }
111
112     return QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
113 }
114
115 static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
116 {
117     if (to < 0)
118         to = run.length();
119
120     QPainter *p = ctx->platformContext();
121
122     QPen textFillPen;
123     if (ctx->textDrawingMode() & TextModeFill)
124         textFillPen = fillPenForContext(ctx);
125
126     QPen textStrokePen;
127     if (ctx->textDrawingMode() & TextModeStroke)
128         textStrokePen = strokePenForContext(ctx);
129
130     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
131     QString string = fromRawDataWithoutRef(sanitized);
132     QPointF pt(point.x(), point.y());
133
134     if (from > 0 || to < run.length()) {
135         if (isComplexText) {
136             QTextLayout layout(string, font);
137             QTextLine line = setupLayout(&layout, run);
138             float x1 = line.cursorToX(from);
139             float x2 = line.cursorToX(to);
140             if (x2 < x1)
141                 qSwap(x1, x2);
142
143             QFontMetrics fm(font);
144             int ascent = fm.ascent();
145             QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
146             QRectF clip = boundingRect;
147
148             ShadowBlur* ctxShadow = ctx->shadowBlur();
149             if (ctxShadow->type() != ShadowBlur::NoShadow) {
150                 const QPointF shadowOffset(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height());
151                 qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
152                 if (shadowOffset.x() > 0)
153                     dx2 = shadowOffset.x();
154                 else
155                     dx1 = -shadowOffset.x();
156                 if (shadowOffset.y() > 0)
157                     dy2 = shadowOffset.y();
158                 else
159                     dy1 = -shadowOffset.y();
160                 // expand the clip rect to include the text shadow as well
161                 const float blurDistance = ctx->state().shadowBlur;
162                 clip.adjust(dx1, dx2, dy1, dy2);
163                 clip.adjust(-blurDistance, -blurDistance, blurDistance, blurDistance);
164             }
165             p->save();
166             p->setClipRect(clip.toRect(), Qt::IntersectClip);
167             pt.setY(pt.y() - ascent);
168
169             if (ctxShadow->type() != ShadowBlur::NoShadow) {
170                 ShadowBlur* ctxShadow = ctx->shadowBlur();
171                 if (ctxShadow->type() != ShadowBlur::BlurShadow
172                     && (!ctxShadow->shadowsIgnoreTransforms() || ctx->getCTM().isIdentity())) {
173                     p->save();
174                     p->setPen(ctx->state().shadowColor);
175                     p->translate(QPointF(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height()));
176                     line.draw(p, pt);
177                     p->restore();
178                 } else {
179                     GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
180                     if (shadowContext) {
181                         QPainter* shadowPainter = shadowContext->platformContext();
182                         // Since it will be blurred anyway, we don't care about render hints.
183                         shadowPainter->setFont(p->font());
184                         shadowPainter->setPen(ctx->state().shadowColor);
185                         line.draw(shadowPainter, pt);
186                         ctxShadow->endShadowLayer(ctx);
187                     }
188                 }
189             }
190             p->setPen(textFillPen);
191             line.draw(p, pt);
192             p->restore();
193             return;
194         }
195         int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
196         pt.setX(pt.x() + skipWidth);
197         string = fromRawDataWithoutRef(sanitized, from, to - from);
198     }
199
200     p->setFont(font);
201
202     int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
203     if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
204         flags |= Qt::TextBypassShaping;
205
206     QPainterPath textStrokePath;
207     if (ctx->textDrawingMode() & TextModeStroke)
208         textStrokePath.addText(pt, font, string);
209
210     ShadowBlur* ctxShadow = ctx->shadowBlur();
211     if (ctx->hasShadow() && ctxShadow->type() != ShadowBlur::NoShadow) {
212         if (ctx->textDrawingMode() & TextModeFill) {
213             if (ctxShadow->type() != ShadowBlur::BlurShadow) {
214                 p->save();
215                 p->setPen(ctx->state().shadowColor);
216                 p->translate(QPointF(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height()));
217                 p->drawText(pt, string, flags, run.expansion());
218                 p->restore();
219             } else {
220                 QFontMetrics fm(font);
221                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
222                 GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
223                 if (shadowContext) {
224                     QPainter* shadowPainter = shadowContext->platformContext();
225                     // Since it will be blurred anyway, we don't care about render hints.
226                     shadowPainter->setFont(p->font());
227                     shadowPainter->setPen(ctx->state().shadowColor);
228                     shadowPainter->drawText(pt, string, flags, run.expansion());
229                     ctxShadow->endShadowLayer(ctx);
230                 }
231             }
232         } else if (ctx->textDrawingMode() & TextModeStroke) {
233             if (ctxShadow->type() != ShadowBlur::BlurShadow) {
234                 const QPointF shadowOffset(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height());
235                 p->translate(shadowOffset);
236                 p->strokePath(textStrokePath, QPen(ctx->state().shadowColor));
237                 p->translate(-shadowOffset);
238             } else {
239                 QFontMetrics fm(font);
240                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
241                 GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
242                 if (shadowContext) {
243                     QPainter* shadowPainter = shadowContext->platformContext();
244                     // Since it will be blurred anyway, we don't care about render hints.
245                     shadowPainter->setFont(p->font());
246                     shadowPainter->strokePath(textStrokePath, QPen(ctx->state().shadowColor));
247                     ctxShadow->endShadowLayer(ctx);
248                 }
249             }
250         }
251     }
252
253     if (ctx->textDrawingMode() & TextModeStroke)
254         p->strokePath(textStrokePath, textStrokePen);
255
256     if (ctx->textDrawingMode() & TextModeFill) {
257         QPen previousPen = p->pen();
258         p->setPen(textFillPen);
259         p->drawText(pt, string, flags, run.expansion());
260         p->setPen(previousPen);
261     }
262 }
263
264 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
265 {
266     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
267 }
268
269 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
270 {
271     if (!primaryFont()->platformData().size())
272         return 0;
273
274     if (!run.length())
275         return 0;
276
277     if (run.length() == 1 && treatAsSpace(run[0]))
278         return QFontMetrics(font()).width(space) + run.expansion();
279
280     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
281     QString string = fromRawDataWithoutRef(sanitized);
282
283     int w = QFontMetrics(font()).width(string);
284     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
285     if (treatAsSpace(run[0]))
286         w -= m_wordSpacing;
287
288     return w + run.expansion();
289 }
290
291 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
292 {
293     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
294     QString string = fromRawDataWithoutRef(sanitized);
295
296     QTextLayout layout(string, font());
297     QTextLine line = setupLayout(&layout, run);
298     return line.xToCursor(position);
299 }
300
301 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
302 {
303     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
304     QString string = fromRawDataWithoutRef(sanitized);
305
306     QTextLayout layout(string, font());
307     QTextLine line = setupLayout(&layout, run);
308
309     float x1 = line.cursorToX(from);
310     float x2 = line.cursorToX(to);
311     if (x2 < x1)
312         qSwap(x1, x2);
313
314     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
315 }
316
317 bool Font::canReturnFallbackFontsForComplexText()
318 {
319     return false;
320 }
321
322 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
323 {
324     notImplemented();
325 }
326
327 #if HAVE(QRAWFONT)
328 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
329 {
330     if (context->paintingDisabled())
331         return;
332
333     bool shouldFill = context->textDrawingMode() & TextModeFill;
334     bool shouldStroke = context->textDrawingMode() & TextModeStroke;
335
336     // Stroking text should always take the complex path.
337     ASSERT(!shouldStroke);
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     ShadowBlur* shadow = context->shadowBlur();
370     switch (shadow->type()) {
371     case ShadowBlur::SolidShadow: {
372         QPen previousPen = painter->pen();
373         painter->setPen(context->state().shadowColor);
374         const QPointF shadowOffset(context->state().shadowOffset.width(), context->state().shadowOffset.height());
375         painter->translate(shadowOffset);
376         painter->drawGlyphRun(point, qtGlyphs);
377         painter->translate(-shadowOffset);
378         painter->setPen(previousPen);
379         break;
380     }
381     case ShadowBlur::BlurShadow: {
382         qreal height = rawFont.ascent() + rawFont.descent() + 1;
383         QRectF boundingRect(point.x(), point.y() - rawFont.ascent(), width, height);
384         GraphicsContext* shadowContext = shadow->beginShadowLayer(context, boundingRect);
385         if (shadowContext) {
386             QPainter* shadowPainter = shadowContext->platformContext();
387             shadowPainter->setPen(context->state().shadowColor);
388             shadowPainter->drawGlyphRun(point, qtGlyphs);
389             shadow->endShadowLayer(context);
390         }
391         break;
392     }
393     case ShadowBlur::NoShadow:
394         break;
395     default:
396         ASSERT_NOT_REACHED();
397         break;
398     }
399
400     QPen previousPen = painter->pen();
401     painter->setPen(fillPenForContext(context));
402     painter->drawGlyphRun(point, qtGlyphs);
403     painter->setPen(previousPen);
404 }
405
406 bool Font::canExpandAroundIdeographsInComplexText()
407 {
408     return false;
409 }
410
411 #else // !HAVE(QRAWFONT)
412
413 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
414 {
415     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
416 }
417
418 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
419 {
420     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
421     QString string = fromRawDataWithoutRef(sanitized);
422
423     QFontMetrics fm(font());
424     float delta = position;
425     int curPos = 0;
426     do {
427         float charWidth = fm.width(string[curPos]);
428         delta -= charWidth;
429         if (includePartialGlyphs) {
430             if (delta + charWidth / 2 <= 0)
431                 break;
432         } else {
433             if (delta + charWidth <= 0)
434                 break;
435         }
436     } while (++curPos < string.size());
437
438     return curPos;
439 }
440
441
442 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
443 {
444     if (!primaryFont()->platformData().size())
445         return 0;
446
447     if (!run.length())
448         return 0;
449
450     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
451     QString string = fromRawDataWithoutRef(sanitized);
452
453     int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
454
455     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
456     if (treatAsSpace(run[0]))
457         w -= m_wordSpacing;
458
459     return w + run.expansion();
460 }
461
462
463 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
464 {
465     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
466     QString wholeText = fromRawDataWithoutRef(sanitized);
467     QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from));
468
469     int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
470     int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
471
472     return FloatRect(pt.x() + startX, pt.y(), width, h);
473 }
474
475 bool Font::canExpandAroundIdeographsInComplexText()
476 {
477     return false;
478 }
479
480 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
481 {
482     notImplemented();
483     return true;
484 }
485
486 int Font::emphasisMarkAscent(const AtomicString&) const
487 {
488     notImplemented();
489     return 0;
490 }
491
492 int Font::emphasisMarkDescent(const AtomicString&) const
493 {
494     notImplemented();
495     return 0;
496 }
497
498 int Font::emphasisMarkHeight(const AtomicString&) const
499 {
500     notImplemented();
501     return 0;
502 }
503
504 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
505 {
506     notImplemented();
507 }
508 #endif // HAVE(QRAWFONT)
509
510 QFont Font::font() const
511 {
512     QFont f = primaryFont()->getQtFont();
513     if (m_letterSpacing != 0)
514         f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
515     if (m_wordSpacing != 0)
516         f.setWordSpacing(m_wordSpacing);
517     return f;
518 }
519
520
521 }
522