a5a16e0fa032da367086d213e0b4d38a5ccfb24f
[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 "ContextShadow.h"
27 #include "FontDescription.h"
28 #include "FontFallbackList.h"
29 #include "FontSelector.h"
30 #if HAVE(QRAWFONT)
31 #include "GlyphBuffer.h"
32 #endif
33 #include "Gradient.h"
34 #include "GraphicsContext.h"
35 #include "NotImplemented.h"
36 #include "Pattern.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             ContextShadow* ctxShadow = ctx->contextShadow();
146
147             if (ctxShadow->m_type != ContextShadow::NoShadow) {
148                 qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
149                 if (ctxShadow->offset().x() > 0)
150                     dx2 = ctxShadow->offset().x();
151                 else
152                     dx1 = -ctxShadow->offset().x();
153                 if (ctxShadow->offset().y() > 0)
154                     dy2 = ctxShadow->offset().y();
155                 else
156                     dy1 = -ctxShadow->offset().y();
157                 // expand the clip rect to include the text shadow as well
158                 clip.adjust(dx1, dx2, dy1, dy2);
159                 clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance);
160             }
161             p->save();
162             p->setClipRect(clip.toRect(), Qt::IntersectClip);
163             pt.setY(pt.y() - ascent);
164
165             if (ctxShadow->m_type != ContextShadow::NoShadow) {
166                 ContextShadow* ctxShadow = ctx->contextShadow();
167                 if (!ctxShadow->mustUseContextShadow(ctx)) {
168                     p->save();
169                     p->setPen(ctxShadow->m_color);
170                     p->translate(ctxShadow->offset());
171                     line.draw(p, pt);
172                     p->restore();
173                 } else {
174                     QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
175                     if (shadowPainter) {
176                         // Since it will be blurred anyway, we don't care about render hints.
177                         shadowPainter->setFont(p->font());
178                         shadowPainter->setPen(ctxShadow->m_color);
179                         line.draw(shadowPainter, pt);
180                         ctxShadow->endShadowLayer(ctx);
181                     }
182                 }
183             }
184             p->setPen(textFillPen);
185             line.draw(p, pt);
186             p->restore();
187             return;
188         }
189         int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
190         pt.setX(pt.x() + skipWidth);
191         string = fromRawDataWithoutRef(sanitized, from, to - from);
192     }
193
194     p->setFont(font);
195
196     int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
197     if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
198         flags |= Qt::TextBypassShaping;
199
200     QPainterPath textStrokePath;
201     if (ctx->textDrawingMode() & TextModeStroke)
202         textStrokePath.addText(pt, font, string);
203
204     ContextShadow* ctxShadow = ctx->contextShadow();
205     if (ctxShadow->m_type != ContextShadow::NoShadow) {
206         if (ctx->textDrawingMode() & TextModeFill) {
207             if (ctxShadow->m_type != ContextShadow::BlurShadow) {
208                 p->save();
209                 p->setPen(ctxShadow->m_color);
210                 p->translate(ctxShadow->offset());
211                 p->drawText(pt, string, flags, run.expansion());
212                 p->restore();
213             } else {
214                 QFontMetrics fm(font);
215                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
216                 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
217                 if (shadowPainter) {
218                     // Since it will be blurred anyway, we don't care about render hints.
219                     shadowPainter->setFont(p->font());
220                     shadowPainter->setPen(ctxShadow->m_color);
221                     shadowPainter->drawText(pt, string, flags, run.expansion());
222                     ctxShadow->endShadowLayer(ctx);
223                 }
224             }
225         } else if (ctx->textDrawingMode() & TextModeStroke) {
226             if (ctxShadow->m_type != ContextShadow::BlurShadow) {
227                 p->translate(ctxShadow->offset());
228                 p->strokePath(textStrokePath, QPen(ctxShadow->m_color));
229                 p->translate(-ctxShadow->offset());
230             } else {
231                 QFontMetrics fm(font);
232                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
233                 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
234                 if (shadowPainter) {
235                     // Since it will be blurred anyway, we don't care about render hints.
236                     shadowPainter->setFont(p->font());
237                     shadowPainter->strokePath(textStrokePath, QPen(ctxShadow->m_color));
238                     ctxShadow->endShadowLayer(ctx);
239                 }
240             }
241         }
242     }
243
244     if (ctx->textDrawingMode() & TextModeStroke)
245         p->strokePath(textStrokePath, textStrokePen);
246
247     if (ctx->textDrawingMode() & TextModeFill) {
248         QPen previousPen = p->pen();
249         p->setPen(textFillPen);
250         p->drawText(pt, string, flags, run.expansion());
251         p->setPen(previousPen);
252     }
253 }
254
255 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
256 {
257     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
258 }
259
260 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
261 {
262     if (!primaryFont()->platformData().size())
263         return 0;
264
265     if (!run.length())
266         return 0;
267
268     if (run.length() == 1 && treatAsSpace(run[0]))
269         return QFontMetrics(font()).width(space) + run.expansion();
270
271     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
272     QString string = fromRawDataWithoutRef(sanitized);
273
274     int w = QFontMetrics(font()).width(string);
275     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
276     if (treatAsSpace(run[0]))
277         w -= m_wordSpacing;
278
279     return w + run.expansion();
280 }
281
282 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
283 {
284     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
285     QString string = fromRawDataWithoutRef(sanitized);
286
287     QTextLayout layout(string, font());
288     QTextLine line = setupLayout(&layout, run);
289     return line.xToCursor(position);
290 }
291
292 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
293 {
294     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
295     QString string = fromRawDataWithoutRef(sanitized);
296
297     QTextLayout layout(string, font());
298     QTextLine line = setupLayout(&layout, run);
299
300     float x1 = line.cursorToX(from);
301     float x2 = line.cursorToX(to);
302     if (x2 < x1)
303         qSwap(x1, x2);
304
305     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
306 }
307
308 bool Font::canReturnFallbackFontsForComplexText()
309 {
310     return false;
311 }
312
313 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
314 {
315     notImplemented();
316 }
317
318 #if HAVE(QRAWFONT)
319 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
320 {
321     if (context->paintingDisabled())
322         return;
323
324     bool shouldFill = context->textDrawingMode() & TextModeFill;
325     bool shouldStroke = context->textDrawingMode() & TextModeStroke;
326
327     // Stroking text should always take the complex path.
328     ASSERT(!shouldStroke);
329
330     // Shadowed text should always take the complex path.
331     ASSERT(context->contextShadow()->m_type == ContextShadow::NoShadow);
332
333     if (!shouldFill && !shouldStroke)
334         return;
335
336     QVector<quint32> glyphIndexes;
337     QVector<QPointF> positions;
338
339     glyphIndexes.reserve(numGlyphs);
340     positions.reserve(numGlyphs);
341
342     float width = 0;
343
344     for (int i = 0; i < numGlyphs; ++i) {
345         Glyph glyph = glyphBuffer.glyphAt(from + i);
346         float advance = glyphBuffer.advanceAt(from + i);
347         if (!glyph)
348             continue;
349         glyphIndexes.append(glyph);
350         positions.append(QPointF(width, 0));
351         width += advance;
352     }
353
354     QRawFont rawFont(fontData->platformData().rawFont());
355
356     QGlyphRun qtGlyphs;
357     qtGlyphs.setGlyphIndexes(glyphIndexes);
358     qtGlyphs.setPositions(positions);
359     qtGlyphs.setRawFont(rawFont);
360
361     QPainter* painter = context->platformContext();
362
363     ContextShadow* shadow = context->contextShadow();
364     switch (shadow->m_type) {
365     case ContextShadow::SolidShadow: {
366         QPen previousPen = painter->pen();
367         painter->setPen(shadow->m_color);
368         painter->translate(shadow->offset());
369         painter->drawGlyphRun(point, qtGlyphs);
370         painter->translate(-shadow->offset());
371         painter->setPen(previousPen);
372         break;
373     }
374     case ContextShadow::BlurShadow: {
375         qreal height = rawFont.ascent() + rawFont.descent() + 1;
376         QRectF boundingRect(point.x(), point.y() - rawFont.ascent(), width, height);
377         QPainter* shadowPainter = shadow->beginShadowLayer(context, boundingRect);
378         if (shadowPainter) {
379             shadowPainter->setPen(shadow->m_color);
380             shadowPainter->drawGlyphRun(point, qtGlyphs);
381             shadow->endShadowLayer(context);
382         }
383         break;
384     }
385     case ContextShadow::NoShadow:
386         break;
387     default:
388         ASSERT_NOT_REACHED();
389         break;
390     }
391
392     QPen previousPen = painter->pen();
393     painter->setPen(fillPenForContext(context));
394     painter->drawGlyphRun(point, qtGlyphs);
395     painter->setPen(previousPen);
396 }
397
398 bool Font::canExpandAroundIdeographsInComplexText()
399 {
400     return false;
401 }
402
403 #else // !HAVE(QRAWFONT)
404
405 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
406 {
407     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
408 }
409
410 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
411 {
412     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
413     QString string = fromRawDataWithoutRef(sanitized);
414
415     QFontMetrics fm(font());
416     float delta = position;
417     int curPos = 0;
418     do {
419         float charWidth = fm.width(string[curPos]);
420         delta -= charWidth;
421         if (includePartialGlyphs) {
422             if (delta + charWidth / 2 <= 0)
423                 break;
424         } else {
425             if (delta + charWidth <= 0)
426                 break;
427         }
428     } while (++curPos < string.size());
429
430     return curPos;
431 }
432
433
434 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
435 {
436     if (!primaryFont()->platformData().size())
437         return 0;
438
439     if (!run.length())
440         return 0;
441
442     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
443     QString string = fromRawDataWithoutRef(sanitized);
444
445     int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
446
447     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
448     if (treatAsSpace(run[0]))
449         w -= m_wordSpacing;
450
451     return w + run.expansion();
452 }
453
454
455 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
456 {
457     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
458     QString wholeText = fromRawDataWithoutRef(sanitized);
459     QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from));
460
461     int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
462     int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
463
464     return FloatRect(pt.x() + startX, pt.y(), width, h);
465 }
466
467 bool Font::canExpandAroundIdeographsInComplexText()
468 {
469     return false;
470 }
471
472 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
473 {
474     notImplemented();
475     return true;
476 }
477
478 int Font::emphasisMarkAscent(const AtomicString&) const
479 {
480     notImplemented();
481     return 0;
482 }
483
484 int Font::emphasisMarkDescent(const AtomicString&) const
485 {
486     notImplemented();
487     return 0;
488 }
489
490 int Font::emphasisMarkHeight(const AtomicString&) const
491 {
492     notImplemented();
493     return 0;
494 }
495
496 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
497 {
498     notImplemented();
499 }
500 #endif // HAVE(QRAWFONT)
501
502 QFont Font::font() const
503 {
504     QFont f = primaryFont()->getQtFont();
505     if (m_letterSpacing != 0)
506         f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
507     if (m_wordSpacing != 0)
508         f.setWordSpacing(m_wordSpacing);
509     return f;
510 }
511
512
513 }
514