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>
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.
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.
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.
25 #include "AffineTransform.h"
26 #include "FontDescription.h"
27 #include "FontFallbackList.h"
28 #include "FontSelector.h"
30 #include "GlyphBuffer.h"
33 #include "GraphicsContext.h"
34 #include "NotImplemented.h"
36 #include "ShadowBlur.h"
41 #include <QFontMetrics>
43 #include <QPainterPath>
48 #include <QTextLayout>
49 #include <qalgorithms.h>
56 static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1)
59 len = string.length() - start;
60 Q_ASSERT(start + len <= string.length());
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);
67 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
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());
82 static QPen fillPenForContext(GraphicsContext* ctx)
84 if (ctx->fillGradient()) {
85 QBrush brush(*ctx->fillGradient()->platformGradient());
86 brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
87 return QPen(brush, 0);
90 if (ctx->fillPattern()) {
91 AffineTransform affine;
92 return QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
95 return QPen(QColor(ctx->fillColor()));
98 static QPen strokePenForContext(GraphicsContext* ctx)
100 if (ctx->strokeGradient()) {
101 QBrush brush(*ctx->strokeGradient()->platformGradient());
102 brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
103 return QPen(brush, ctx->strokeThickness());
106 if (ctx->strokePattern()) {
107 AffineTransform affine;
108 QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
109 return QPen(brush, ctx->strokeThickness());
112 return QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
115 static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
120 QPainter *p = ctx->platformContext();
123 if (ctx->textDrawingMode() & TextModeFill)
124 textFillPen = fillPenForContext(ctx);
127 if (ctx->textDrawingMode() & TextModeStroke)
128 textStrokePen = strokePenForContext(ctx);
130 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
131 QString string = fromRawDataWithoutRef(sanitized);
132 QPointF pt(point.x(), point.y());
134 if (from > 0 || to < run.length()) {
136 QTextLayout layout(string, font);
137 QTextLine line = setupLayout(&layout, run);
138 float x1 = line.cursorToX(from);
139 float x2 = line.cursorToX(to);
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;
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();
155 dx1 = -shadowOffset.x();
156 if (shadowOffset.y() > 0)
157 dy2 = shadowOffset.y();
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);
166 p->setClipRect(clip.toRect(), Qt::IntersectClip);
167 pt.setY(pt.y() - ascent);
169 if (ctxShadow->type() != ShadowBlur::NoShadow) {
170 ShadowBlur* ctxShadow = ctx->shadowBlur();
171 if (ctxShadow->type() != ShadowBlur::BlurShadow
172 && (!ctxShadow->shadowsIgnoreTransforms() || ctx->getCTM().isIdentity())) {
174 p->setPen(ctx->state().shadowColor);
175 p->translate(QPointF(ctx->state().shadowOffset.width(), ctx->state().shadowOffset.height()));
179 GraphicsContext* shadowContext = ctxShadow->beginShadowLayer(ctx, boundingRect);
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);
190 p->setPen(textFillPen);
195 int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
196 pt.setX(pt.x() + skipWidth);
197 string = fromRawDataWithoutRef(sanitized, from, to - from);
202 int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
203 if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
204 flags |= Qt::TextBypassShaping;
206 QPainterPath textStrokePath;
207 if (ctx->textDrawingMode() & TextModeStroke)
208 textStrokePath.addText(pt, font, string);
210 ShadowBlur* ctxShadow = ctx->shadowBlur();
211 if (ctx->hasShadow() && ctxShadow->type() != ShadowBlur::NoShadow) {
212 if (ctx->textDrawingMode() & TextModeFill) {
213 if (ctxShadow->type() != ShadowBlur::BlurShadow) {
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());
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);
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);
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);
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);
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);
253 if (ctx->textDrawingMode() & TextModeStroke)
254 p->strokePath(textStrokePath, textStrokePen);
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);
264 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
266 drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
269 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
271 if (!primaryFont()->platformData().size())
277 if (run.length() == 1 && treatAsSpace(run[0]))
278 return QFontMetrics(font()).width(space) + run.expansion();
280 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
281 QString string = fromRawDataWithoutRef(sanitized);
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]))
288 return w + run.expansion();
291 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
293 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
294 QString string = fromRawDataWithoutRef(sanitized);
296 QTextLayout layout(string, font());
297 QTextLine line = setupLayout(&layout, run);
298 return line.xToCursor(position);
301 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
303 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
304 QString string = fromRawDataWithoutRef(sanitized);
306 QTextLayout layout(string, font());
307 QTextLine line = setupLayout(&layout, run);
309 float x1 = line.cursorToX(from);
310 float x2 = line.cursorToX(to);
314 return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
317 bool Font::canReturnFallbackFontsForComplexText()
322 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
328 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
330 if (context->paintingDisabled())
333 bool shouldFill = context->textDrawingMode() & TextModeFill;
334 bool shouldStroke = context->textDrawingMode() & TextModeStroke;
336 // Stroking text should always take the complex path.
337 ASSERT(!shouldStroke);
339 if (!shouldFill && !shouldStroke)
342 QVector<quint32> glyphIndexes;
343 QVector<QPointF> positions;
345 glyphIndexes.reserve(numGlyphs);
346 positions.reserve(numGlyphs);
350 for (int i = 0; i < numGlyphs; ++i) {
351 Glyph glyph = glyphBuffer.glyphAt(from + i);
352 float advance = glyphBuffer.advanceAt(from + i);
355 glyphIndexes.append(glyph);
356 positions.append(QPointF(width, 0));
360 QRawFont rawFont(fontData->platformData().rawFont());
363 qtGlyphs.setGlyphIndexes(glyphIndexes);
364 qtGlyphs.setPositions(positions);
365 qtGlyphs.setRawFont(rawFont);
367 QPainter* painter = context->platformContext();
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);
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);
386 QPainter* shadowPainter = shadowContext->platformContext();
387 shadowPainter->setPen(context->state().shadowColor);
388 shadowPainter->drawGlyphRun(point, qtGlyphs);
389 shadow->endShadowLayer(context);
393 case ShadowBlur::NoShadow:
396 ASSERT_NOT_REACHED();
400 QPen previousPen = painter->pen();
401 painter->setPen(fillPenForContext(context));
402 painter->drawGlyphRun(point, qtGlyphs);
403 painter->setPen(previousPen);
406 bool Font::canExpandAroundIdeographsInComplexText()
411 #else // !HAVE(QRAWFONT)
413 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
415 drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
418 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
420 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
421 QString string = fromRawDataWithoutRef(sanitized);
423 QFontMetrics fm(font());
424 float delta = position;
427 float charWidth = fm.width(string[curPos]);
429 if (includePartialGlyphs) {
430 if (delta + charWidth / 2 <= 0)
433 if (delta + charWidth <= 0)
436 } while (++curPos < string.size());
442 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
444 if (!primaryFont()->platformData().size())
450 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
451 QString string = fromRawDataWithoutRef(sanitized);
453 int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
455 // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
456 if (treatAsSpace(run[0]))
459 return w + run.expansion();
463 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
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));
469 int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
470 int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
472 return FloatRect(pt.x() + startX, pt.y(), width, h);
475 bool Font::canExpandAroundIdeographsInComplexText()
480 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
486 int Font::emphasisMarkAscent(const AtomicString&) const
492 int Font::emphasisMarkDescent(const AtomicString&) const
498 int Font::emphasisMarkHeight(const AtomicString&) const
504 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
508 #endif // HAVE(QRAWFONT)
510 QFont Font::font() const
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);